Quickstart

Deploy a function and debug a production bug in 5 minutes.

What you'll build
A user signup function that writes to Postgres, sends an email, and charges Stripe. You'll introduce a deliberate failure, spot it with flux tail, and root-cause it with flux why.
Coming from Express / FastAPI / Rails?
Flux is a runtime — your code runs inside it. That's what makes automatic recording possible. But your business logic stays the same: req.body becomes input, res.json() becomes return, db.query() becomes ctx.db.query(). You can start with one endpoint and keep your existing backend running alongside Flux.

1. Install the CLI

$ curl -fsSL https://fluxbase.co/install | bash

    flux 1.0.0 installed to /usr/local/bin/flux

2. Initialise a project

$ flux init my-backend
$ cd my-backend
$ ls

  functions/
    create_user.ts
    send_welcome.ts
  flux.toml

3. Look at the function

Open functions/create_user.ts:

export default defineFunction({
  name: "create_user",

  handler: async ({ input, ctx }) => {
    // validate
    if (!input.email) throw new Error("email required");

    // write to database
    const [user] = await ctx.db.query({
      table: "users",
      operation: "insert",
      data: { email: input.email, plan: "free" },
    });

    // trigger async welcome email
    await ctx.queue.push("send_welcome", { userId: user.id });

    return { userId: user.id, status: "ok" };
  },
});

Notice: no logging setup, no trace SDK, no middleware. The runtime instruments everything automatically.

4. Deploy

$ flux deploy

  Deploying 2 functions…

    create_user   → localhost:4000/create_user
    send_welcome  (async)

    Deployed in 18s  deploy:d_7f3a9

5. Trigger a request

In a second terminal, run flux tail to watch live:

$ flux tail

  Streaming requests…

In your original terminal:

$ curl -X POST https://localhost:4000/create_user     -H "Content-Type: application/json"     -d '{"email":"alice@example.com"}'

  {"userId":"u_42","status":"ok"}
  x-request-id: 4f9a3b2c

Your flux tail terminal shows:

    POST /create_user  201  88ms  req:4f9a3b2c

6. Introduce a failure (deliberate)

Simulate what happens when Stripe is down. Add a timeout to create_user.ts:

// add before return:
await ctx.tools.call("stripe.charge", {
  amount: 0,  // <— this will timeout (demo)
  currency: "usd",
});
$ flux deploy

Call it again:

$ curl -X POST https://localhost:4000/create_user     -d '{"email":"bob@example.com"}'

  {"error":"internal_error"}

flux tail shows:

    POST /create_user  500  10044ms  req:550e8400
     └─ stripe.charge timeout

7. Root-cause it

$ flux why 550e8400

  ROOT CAUSE
  stripe.charge timed out after 10s

  LOCATION
  functions/create_user.ts:18

  SPAN
  stripe.charge  POST /v1/charges  10002ms  ✗ timeout

  DATABASE CHANGES
  users email=bob@example.com, plan=free  (inserted, then rolled back)

  SUGGESTION
  → Add a 5s timeout and retry with idempotency key
You just debugged a production failure in one command.
Root cause, exact file and line, data changes, an actionable suggestion. Without looking at any logs.

8. Go deeper

See the full span tree:

$ flux trace 550e8400

  Trace 550e8400  POST /create_user  500

  ▸ gateway                    4ms
    auth ✔  rate_limit ✔
  ▸ create_user              10044ms
    ▸ db:insert(users)          14ms
    ▸ stripe.charge          10002ms  ✗ timeout
  ▸ send_welcome  SKIP  (not queued — request failed)

  ── total: 10052ms ───────────────────

Compare the failing request to the passing one:

$ flux trace diff 4f9a3b2c 550e8400

  SPAN             BEFORE   AFTER     DELTA
  gateway             3ms     4ms      +1ms
  create_user        81ms  10044ms  +9963ms  ✗
  stripe.charge      12ms  10002ms  +9990ms  ✗

See what the failed request wrote to the database before it was rolled back:

$ flux state history users --id 43

  users id=43  (2 mutations)

  2026-03-10 14:25:00  INSERT  email=bob@example.com, plan=free   req:550e8400
  2026-03-10 14:25:01  DELETE  (rolled back)                        req:550e8400

Next steps