> ## 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 an Express app on floo

> End-to-end guide. Deploy an Express (Node.js) app, add Postgres, add per-user auth, and ship a custom domain.

This guide walks an Express 4/5 app from local code to a production URL with a database and per-user auth. Every step has runnable JavaScript 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:

* An Express project (or `npm init -y && npm install express`).
* The project pushed to a GitHub repository.
* The floo CLI installed and authenticated (`curl -fsSL https://getfloo.com/install.sh | bash` then `floo auth login`).

## 1. Add a Dockerfile

```dockerfile Dockerfile theme={null}
FROM node:20-slim AS deps
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm ci --omit=dev

FROM node:20-slim AS runtime
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=3000
COPY --from=deps /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
```

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

## 2. Trust the proxy

floo's edge sits in front of your Express app. Tell Express to trust it so `req.protocol`, `req.hostname`, and `req.ip` reflect the original client request:

```js server.js theme={null}
import express from "express";

const app = express();
app.set("trust proxy", true);
app.use(express.json());

app.get("/health", (_req, res) => res.send("ok"));
app.get("/", (_req, res) => res.send("hello from express on floo"));

const port = Number(process.env.PORT ?? 3000);
app.listen(port, "0.0.0.0", () => console.log(`listening on ${port}`));
```

## 3. Initialize the floo config

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

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

[services.web]
type = "web"
path = "."
port = 3000
ingress = "public"
dev_command = "node --watch server.js"
migrate_command = "node ./scripts/migrate.js"   # optional
```

## 4. 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-express-app
floo deploys watch --app my-express-app
floo apps status my-express-app
```

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

## 5. Add a Postgres database

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

`DATABASE_URL` is injected. With `pg`:

```js db.js theme={null}
import pg from "pg";

export const pool = new pg.Pool({
  connectionString: process.env.DATABASE_URL,
  max: 10,
});
```

Or use Prisma (`npx prisma init`); Prisma reads `DATABASE_URL` from the environment automatically.

## 6. 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-express-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 Express app.

Read the headers in any handler:

```js server.js theme={null}
app.use((req, _res, next) => {
  const email = req.get("x-floo-user-email");
  const id = req.get("x-floo-user-id");
  const name = req.get("x-floo-user-name");
  req.flooUser = email ? { id, email, name } : null;
  next();
});

app.get("/dashboard", (req, res) => {
  if (!req.flooUser) return res.sendStatus(401);
  res.json({ hello: req.flooUser.email });
});
```

For local development, send the headers yourself or extend the middleware to inject a fixture user when they're missing.

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

## 7. Add a custom domain

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

Add the CNAME shown at your DNS provider. With `app.set("trust proxy", true)`, Express reads `req.hostname` from `X-Forwarded-Host` automatically — no extra config needed.

## 8. Local development with prod data

```bash theme={null}
floo dev --app my-express-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-express-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`.** Express defaults to `0.0.0.0` only when you pass it explicitly to `app.listen(port, "0.0.0.0", ...)`. Without it some setups bind to `localhost`.
* **`app.set("trust proxy", true)` is required.** Without it, `req.protocol` is always `http`, secure cookies don't get set, and `req.ip` is wrong.
* **ESM vs CommonJS.** The examples use ESM (`import`/`export`). For CJS, use `require()` and `module.exports` equivalently — the logic is identical.

## 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 Express alongside a Next.js or React frontend with shared origin.
  </Card>

  <Card title="Custom Domains" href="/guides/custom-domains">
    DNS, verification, multi-service routing.
  </Card>
</CardGroup>
