Skip to main content

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.

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 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
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"]
Bind to 0.0.0.0, not localhost. Cloud Run only routes traffic to processes bound to all interfaces.

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:
server.js
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

floo init my-express-app
floo.app.toml
[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

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
Your Express app is live at https://my-express-app-dev.on.getfloo.com.

5. Add a Postgres database

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:
db.js
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:
floo.app.toml
[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:
server.js
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.

7. Add a custom domain

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

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:
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

Add User Auth — full reference

Identity headers, access policies, and access modes in detail.

Managed Services

Postgres, Redis, Storage — what they cost and how isolation works.

Multi-Service Routing

Deploy Express alongside a Next.js or React frontend with shared origin.

Custom Domains

DNS, verification, multi-service routing.