Skip to content

Use the Sapporta CLI

View as Markdown

The Sapporta CLI is a client for a running application. It discovers tables and documented endpoints, performs generated row operations, calls application routes, and exposes ability-gated SQL for exceptional administration. SQL bypasses generated row helpers even when the caller is allowed to use it.

Use the project-local binary so the commands match the installed framework version:

Terminal window
pnpm exec sapporta --version
pnpm exec sapporta --help
pnpm exec sapporta rows list --help
pnpm exec sapporta rows count --help
pnpm exec sapporta api post --help

Verify nested help before automating a command. The current CLI groups live API work under endpoints, tables, rows, api, and sql; subcommand options remain the authoritative grammar for the installed version.

Run from inside a project and API-backed commands need no URL: the CLI reads SAPPORTA_API_PORT from the project’s .env.development and targets that port on localhost. Outside a project it falls back to http://localhost:3000. Set an explicit URL when the target is not unquestionably the intended application. A protected app also needs a bearer token.

Terminal window
export SAPPORTA_API_URL="https://app.example.com"
read -s SAPPORTA_API_TOKEN
export SAPPORTA_API_TOKEN
pnpm exec sapporta endpoints list

--api-url, --api-token, and --output override SAPPORTA_API_URL, SAPPORTA_API_TOKEN, and SAPPORTA_OUTPUT_FORMAT. Prefer an environment variable or secret injection to --api-token, which can expose the credential in process arguments or logs.

The following commands use an illustrative books table. Replace the table, columns, and ID with values discovered from the target application.

Terminal window
pnpm exec sapporta tables list
pnpm exec sapporta tables show books
pnpm exec sapporta endpoints show "GET /api/tables/books"
pnpm exec sapporta --output json rows list books \
--q "Relativity" \
--limit 10

Resolve the intended row from that visible result. Do not copy an ID from documentation or guess a foreign key. Once exactly one row is identified, set BOOK_ID to that returned value. The shell guard and exact read below confirm the target before the ordinary field update:

Terminal window
: "${BOOK_ID:?Set BOOK_ID from the single inspected row}"
pnpm exec sapporta --output json rows get books "$BOOK_ID"
pnpm exec sapporta rows update books "$BOOK_ID" \
--values '{"author":"Albert Einstein"}'
pnpm exec sapporta --output json rows get books "$BOOK_ID"

Payloads contain fields accepted by the owning operation. Omit server-managed workspace, ownership, role, audit, and row-scope fields rather than trying to change authority from the client.

Use api only after discovery when a named application operation or report owns the task. For example, if the live inventory contains an operation such as POST /api/books/{id}/publish, inspect its exact body and responses before calling its mounted path:

Terminal window
pnpm exec sapporta endpoints show "POST /api/books/{id}/publish"
pnpm exec sapporta --output json \
api post "/api/books/$BOOK_ID/publish" --body '{}'

That route is illustrative, not generated by Sapporta. Use only an application operation returned by endpoints list, then confirm its intended consequence with a harmless read.

Use rows count when a read-only question is a filtered total over one table:

Terminal window
pnpm exec sapporta --output json rows count tasks \
--where '{"status":{"neq":"completed"}}'

The JSON result is explicit about whether it is scalar:

{ "data": { "kind": "total", "count": 8 } }

Add --group-by, --order, and --limit for bounded grouped results:

Terminal window
pnpm exec sapporta --output json rows count tasks \
--where '{"status":{"neq":"completed"}}' \
--group-by project_id \
--order desc \
--limit 10

The count uses the caller’s read ability and row visibility. It does not define what a business word such as “pending” means; map that word to a declared value or use the report that already owns the definition. Grouped foreign keys remain keys, and their labels come from a separate lookup with its own authorization boundary.

Table output is convenient for interactive inspection. Non-TTY output defaults to JSON, but explicit --output json makes automation deterministic. Successful commands return the server’s structured result when one exists. CLI failures in JSON mode use this envelope:

{ "ok": false, "error": "...", "code": "stable_owner_code" }

Branch on code, not error prose. Direct HTTP clients can also branch on the owning status/code pair.

Failure ownerExample codeResponse
CLI target or networkAPP_SERVER_UNREACHABLECheck the URL, running server, and network permission
Shared application authenticationunauthenticated, token_expired, or token_revokedStop and repair the caller authority
Generated table operationROW_NOT_FOUND or a query-validation codeCorrect the visible ID, query, or payload
Application endpointCode declared by that feature contractFollow only the recovery branch owned by that operation

A harmless rows list ... --limit 1 is suitable for checking a credential. Do not test authority with a mutation. Also do not retry a mutation after an ambiguous transport failure until a read shows whether it took effect.

Malformed local input is a safe way to verify the failure envelope without contacting an application:

Terminal window
pnpm exec sapporta --output json rows list books --where '{'
{ "ok": false, "error": "Invalid JSON for --where", "code": "INVALID_JSON" }

The CLI operates the same mounted application surface as the browser. Its value is a repeatable read-back loop and machine-readable output, not a second data model.

Migration commands are project package scripts rather than running-app CLI operations. Use the schema changes and migrations guide instead of inferring migration readiness from a data command.