> ## Documentation Index
> Fetch the complete documentation index at: https://getfloo.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Build a FastAPI app on floo

> End-to-end guide. Deploy a FastAPI app, add Postgres, add per-user auth, and ship a custom domain — all from a real Python project.

This guide walks a FastAPI app from local code to a production URL with a database and per-user auth. Every step has runnable Python code. By the end you have a working app at `https://<app>.on.getfloo.com` with a Postgres sibling service, signed-in users, and (optionally) your own domain.

If you've never deployed to floo before, read [Golden Path](/guides/golden-path) first for the minimal three-command flow.

## Before you start

You need:

* A FastAPI project (or a fresh `uv init` / `poetry new`).
* The project pushed to a GitHub repository. floo pulls source from GitHub — it does not upload local files.
* The floo CLI installed and authenticated (`curl -fsSL https://getfloo.com/install.sh | bash` then `floo auth login`).
* The floo GitHub App installed on your repo's org.

## 1. Add a Dockerfile

```dockerfile Dockerfile theme={null}
FROM python:3.12-slim

ENV PYTHONUNBUFFERED=1 \
    PIP_NO_CACHE_DIR=1 \
    PIP_DISABLE_PIP_VERSION_CHECK=1

RUN apt-get update -qq && \
    apt-get install -y --no-install-recommends build-essential libpq-dev && \
    rm -rf /var/lib/apt/lists/*

WORKDIR /app

COPY pyproject.toml uv.lock* requirements*.txt ./
RUN if [ -f uv.lock ]; then \
      pip install uv && uv export --no-dev --frozen > requirements.txt; \
    fi && \
    pip install -r requirements.txt

COPY . .

EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
```

<Tip>
  Bind to `0.0.0.0`, not `127.0.0.1`. Cloud Run only routes traffic to processes bound to all interfaces.
</Tip>

## 2. Initialize the floo config

```bash theme={null}
floo init my-fastapi-app
```

For a single-service FastAPI app:

```toml floo.app.toml theme={null}
[app]
name = "my-fastapi-app"

[services.web]
type = "web"
path = "."
port = 8000
ingress = "public"
dev_command = "uvicorn app.main:app --reload --port 8000"
migrate_command = "alembic upgrade head"
```

`migrate_command` is optional — set it if you use Alembic. It runs after every deploy (against dev) and after every promote (against prod).

## 3. Connect the repo and deploy

```bash theme={null}
git add . && git commit -m "feat: floo config + Dockerfile"
git push origin main
floo apps github connect owner/my-fastapi-app
floo deploys watch --app my-fastapi-app
```

When the deploy is green:

```bash theme={null}
floo apps status my-fastapi-app
```

<Check>
  **Your FastAPI app is live** at `https://my-fastapi-app-dev.on.getfloo.com`.

  Every `git push origin main` ships to dev. `floo releases promote --app my-fastapi-app` publishes to `https://my-fastapi-app.on.getfloo.com`.
</Check>

## 4. Add a Postgres database

```bash theme={null}
floo services add postgres --app my-fastapi-app
git add .floo/services.lock && git commit -m "feat: add postgres"
git push origin main
```

`DATABASE_URL` is injected into the runtime. Read it with SQLAlchemy:

```python app/db.py theme={null}
import os
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker

DATABASE_URL = os.environ["DATABASE_URL"].replace("postgresql://", "postgresql+asyncpg://", 1)
engine = create_async_engine(DATABASE_URL, pool_pre_ping=True)
SessionLocal = async_sessionmaker(engine, expire_on_commit=False)
```

Dev and prod each get isolated Postgres schemas and credentials — no shared state.

## 5. Add per-user auth

floo manages user authentication for you. Set `access_mode = "accounts"` in `floo.app.toml`:

```toml floo.app.toml theme={null}
[app]
name = "my-fastapi-app"
access_mode = "accounts"
```

Push and deploy. From the next deploy onward, floo's gateway sits in front of your app and:

* Redirects unauthenticated requests to a hosted login page.
* Validates the session cookie on every request.
* Injects identity headers into every request that reaches your FastAPI app.

Your FastAPI handlers read the headers — no auth code, no callback handlers, no session storage:

```python app/main.py theme={null}
from fastapi import FastAPI, Header
from typing import Annotated

app = FastAPI()

@app.get("/me")
async def me(
    email: Annotated[str | None, Header(alias="X-Floo-User-Email")] = None,
    user_id: Annotated[str | None, Header(alias="X-Floo-User-Id")] = None,
    name: Annotated[str | None, Header(alias="X-Floo-User-Name")] = None,
):
    return {"email": email, "id": user_id, "name": name}
```

Or build a dependency that requires a signed-in user:

```python app/auth.py theme={null}
from fastapi import Header, HTTPException, status
from typing import Annotated

class FlooUser:
    def __init__(self, email: str, user_id: str, name: str | None):
        self.email = email
        self.id = user_id
        self.name = name

def require_user(
    email: Annotated[str | None, Header(alias="X-Floo-User-Email")] = None,
    user_id: Annotated[str | None, Header(alias="X-Floo-User-Id")] = None,
    name: Annotated[str | None, Header(alias="X-Floo-User-Name")] = None,
) -> FlooUser:
    if not email or not user_id:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
    return FlooUser(email=email, user_id=user_id, name=name)
```

Use it on any route:

```python theme={null}
from fastapi import Depends

@app.get("/dashboard")
async def dashboard(user: Annotated[FlooUser, Depends(require_user)]):
    return {"hello": user.email}
```

For local development, send the headers yourself with `curl -H 'X-Floo-User-Email: you@example.com' ...` or wrap the dependency to return a fixture user when the headers are missing.

For the full reference on access modes and identity headers, see [Add User Auth to Your App](/guides/app-auth).

## 6. Add a custom domain

```bash theme={null}
floo domains add app.example.com --app my-fastapi-app
```

Add the CNAME shown in the output at your DNS provider. FastAPI doesn't need any extra config — it reads the `Host` and `X-Forwarded-Host` headers from the request.

## 7. Local development with prod data

```bash theme={null}
floo dev --app my-fastapi-app --service web
```

Runs `dev_command` locally with `DATABASE_URL` and other env vars sourced from your dev floo app — real Cloud SQL connection, no credentials in your shell history.

To also test signed-in flows for this accounts-mode app, add `--fixture-user`:

```bash theme={null}
floo dev --app my-fastapi-app --service web --fixture-user you@example.com
```

`floo dev` then starts a small proxy in front of each service that injects the same `X-Floo-User-*` headers floo's gateway adds in production. The output table shows both the raw service URL and the auth-proxied URL — hit the auth-proxied one for any path that reads identity headers.

## Common gotchas

* **`/healthz` is reserved.** Cloud Run's edge intercepts that exact path. Use `/health` or `/livez`.
* **Bind to `0.0.0.0`.** `127.0.0.1` won't accept Cloud Run traffic.
* **Async DB drivers.** Use `asyncpg` for async SQLAlchemy. Use `psycopg2-binary` for sync. Don't mix.
* **`X-Forwarded-Proto`.** floo's edge terminates TLS and forwards `X-Forwarded-Proto: https`. If you build absolute URLs from `request.url.scheme`, use the forwarded scheme instead.

## What's next

<CardGroup cols={2}>
  <Card title="Add User Auth — full reference" href="/guides/app-auth">
    Identity headers, access policies, and access modes in detail.
  </Card>

  <Card title="Managed Services" href="/guides/managed-services">
    Postgres, Redis, Storage — what they cost and how isolation works.
  </Card>

  <Card title="Multi-Service Routing" href="/guides/multi-service-routing">
    Deploy a React or Next.js frontend alongside FastAPI with shared origin.
  </Card>

  <Card title="Environments" href="/guides/environments">
    Dev vs prod, promotion, env overrides.
  </Card>
</CardGroup>
