Browse the docs

Start

Test and debug

Check a capsule without a browser. fuast check before every deploy, fuast run for headless handler calls, inspect --query for one query's reads, logs with source-mapped stacks, the dev type error overlay, and run-many for parallel dev servers.

An agent can verify a capsule end to end from the terminal. The loop is: fuast check until it is clean, fuast dev in one terminal, then fuast run, fuast db dump, and fuast logs in another to prove each feature works. Use a browser for the UI, not to find out whether a mutation wrote a row.

Check before you deploy

fuast check
fuast check --json

check builds the capsule, runs the TypeScript checker on every file, and checks the capsule contract: import rules, capsule() keys, schema, index declarations, cron expressions, tools, and the favicon. It exits with code 1 on any error. fuast deploy runs the same checks before it uploads, and the platform runs them again, so a capsule that fails check never deploys.

Each error names the file, line, and column with the source line underneath. --json prints { ok: false, error, code, diagnostics } where every diagnostic has file, line, column, code, category, message, and a frame. Fix the first error first: later ones are often caused by it.

Type errors in the browser

While fuast dev runs, it type checks the capsule after every save. Build errors and type errors appear in the terminal and as an overlay on the open app page, with the file, line, and source frame. Fix the code and save, and the overlay updates on its own.

A type error does not stop fuast dev: the app keeps running so you can keep testing. It does stop fuast deploy, so clear the overlay before you deploy.

Call handlers headlessly

fuast run calls one query, mutation, or action the way a client would and prints the result as JSON:

fuast run todos                                     # query with no arguments
fuast run addNote '"Buy milk"' 2                    # each argument is one JSON value
fuast run addTodo '{"text":"milk"}'                 # a handler with tool args takes one object
fuast run setTodoDone '{"todoId":"01j9…","done":true}'
fuast run todos --json                              # result plus logs and read metrics
  • Without a slug it calls fuast dev on --port (default 3000) as your local identity: dev, or the name from fuast auth as <name> (no restart needed). Switch to alice, run the same call, and confirm alice cannot see dev's rows. --as <name>, --guest, and --anon change the identity for one call.
  • With a slug (fuast run todos my-app) it calls the deployed app as your logged-in account, with the same sharing rules and daily quotas as the browser.
  • The handler kind comes from the app's manifest, so you never name it. A last argument that is not JSON is the slug or app URL, so quote string arguments: '"milk"'. A slug and --port together are an error, and so is a bare word that cannot be a slug (Hello): the CLI tells you to quote it instead of calling the wrong app.
  • A handler error prints the message and its code and exits with code 1. That makes fuast run usable in scripts and CI.
  • --json prints { ok, kind, name, result, ms, logs, metrics }: the result, the log entries the call wrote, and its database read metrics (the operations of that one run).

A mutation run this way is a real write. Subscribed browser tabs update, and the row is there for fuast db dump.

The same call over HTTP, for scripts that do not use the CLI, is POST <app url>/api/call. See reference.

Measure one query

fuast inspect --query todos --port 3000
fuast inspect --query todos my-app --json

inspect --query <name> prints the database read metrics recorded for that query, per run: the totals of the last run (rows read, rows returned, bytes read, index scans, summed over every database call the run made), then the last few runs, each with its database calls (table and index, rows returned, rows read, bytes read, and whether it was a full scan). With --json, queryExecution.runs holds the same data and observed counts runs. Run the query first (open the page, or fuast run <name>) so there is something to show. Compare read with returned: if a query reads far more rows than it returns, add an index that matches its filter. See limits.

Read logs and stacks

fuast logs --port 3000
fuast logs my-app --follow
fuast logs --json

Every ctx.log call and every handler error lands in the app's log. A handler error entry has level: "error", the message <kind> <name> failed: <error>, the handler name, and data.stack. Stacks are mapped back to your capsule source in fuast dev and on hosted apps alike (every deploy keeps its server source map), so frames read server/index.ts:42:11, not a line in a bundle. Go to the first frame inside server/ or shared/.

fuast dev also prints each log line and error in its own terminal as it happens.

Errors from a query reach the client too: useQuery throws to the nearest ErrorBoundary, and mutation promises reject with the message and code. See errors.

Check the data

fuast db list --port 3000          # tables and row counts
fuast db dump --port 3000          # rows, bounded by the 1000-row return limit
fuast db dump --table todos --json

After every mutation you test, confirm the rows changed the way you expected. Do not assume a write happened because the UI looks right.

Test several people

Locally you are the signed-in account dev. Add ?fuast_user=alice to the URL for a second person in another tab, ?fuast_guest=1 for a guest on a public app. For headless checks, fuast auth as alice, then fuast run, then fuast auth reset. See local identities.

Many dev servers at once

fuast run-many --count 5 --base-port 4000

run-many [dir] starts --count independent dev servers of the same capsule (default 20) on consecutive ports from --base-port (default 4000) and prints their URLs. Each has its own database, so parallel test runs or several agents can work against the same code without sharing state. Stop them all with Ctrl-C.

Before you report done

  1. fuast check passes.
  2. Every handler you wrote was exercised with fuast run or in the browser, and fuast db dump shows the expected rows.
  3. fuast logs has no handler errors you did not expect.
  4. Private data was checked with two identities.
  5. After deploy, the same checks pass against the slug: fuast run <name> <slug>, fuast logs <slug>.