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 --jsoncheck 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 devon--port(default 3000) as your local identity:dev, or the name fromfuast auth as <name>(no restart needed). Switch toalice, run the same call, and confirm alice cannot see dev's rows.--as <name>,--guest, and--anonchange 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--porttogether 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
codeand exits with code 1. That makesfuast runusable in scripts and CI. --jsonprints{ 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 --jsoninspect --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 --jsonEvery 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 --jsonAfter 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 4000run-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
fuast checkpasses.- Every handler you wrote was exercised with
fuast runor in the browser, andfuast db dumpshows the expected rows. fuast logshas no handler errors you did not expect.- Private data was checked with two identities.
- After deploy, the same checks pass against the slug:
fuast run <name> <slug>,fuast logs <slug>.