Skip to main content
Manage SQL databases using the telnyx-edge CLI. Remote commands reach a deployed database through the same primary as the env binding, so remote CLI SQL and SQL run from a deployed function are serialized together. Local commands use an isolated Docker-backed database and do not read or change deployed data.
The storage sqldb family requires CLI v0.3.0 or newer — the first release to carry any SQL surface. export requires CLI v0.5.0 or newer. On v0.2.5 and earlier telnyx-edge storage sqldb fails as an unknown command. Check with telnyx-edge --version.

Database Management

create returns immediately with status: pending; provisioning finishes in the background, typically in 2 to 11 seconds:
Poll get until the status is provision_ok:
list paginates, newest first:
delete takes effect immediately and asks nothing first — there is no confirmation prompt and no --yes flag:
The record disappears shortly afterwards: get and any query against that id return 404. Delete removes the database record and makes the data unreachable through every path; it is not a guaranteed erase of the stored bytes, so it does not satisfy a data-destruction requirement on its own. It also does not touch functions that still declare the id in a [storage.sqldb.<NAME>] block: those functions stay deployed and their queries start failing. Remove the block and redeploy them.

list Flags

get and delete take the database id only. For --remote, execute and the migrations subcommands accept either the id or the database name — see Addressing a Database. In --local mode, the argument is the local database name (or a manifest binding name) and does not trigger an organization lookup.

Running SQL

execute runs SQL against a database without deploying anything — to create a schema, load seed data, or inspect what a function wrote.
A statement that returns no rows prints a confirmation:
A read prints a table and a row count:
Columns are ordered alphabetically rather than by their order in the SELECT list, and a SQL NULL renders as NULL. To learn a new row’s id, or to count the rows a write touched, add RETURNING to the statement and count the rows it prints. SELECT changes() in a separate call reads a counter on a connection shared with every other caller of the database, so another write can land in between. --json prints the raw result object instead of a table — on stderr in the current CLI, so scripts should capture it with 2>&1 >/dev/null rather than piping stdout. results holds the rows and is the only field the service returns — a statement that produces no rows prints {}:
BLOB columns cross this boundary as a JSON array of byte values — a three-byte blob comes back as [9, 8, 7], not base64 and not an object. Decode it as bytes on the client. Writing binary this way still means a SQL hex literal (X'090807'): --param carries strings, numbers, booleans and null, and JSON has no byte-array form to bind. From a function, bind an ArrayBuffer instead.

Binding Values

Pass values with --param instead of writing them into the SQL. Both flags are repeatable and fill the statement’s ? placeholders left to right, in the order they appear on the command line:
--param always sends a string; --param-json parses its argument as one JSON value, which is the only way to bind a number, boolean, or null from a shell — --param 42 binds the text "42", --param-json 42 binds the integer. The count must equal the number of placeholders, or the call is rejected with a 422 naming both counts. Neither flag combines with --file: a script binds to its last statement only, so the CLI refuses rather than binding somewhere you did not choose. Bind anything that came from outside your own script — that is what keeps a quote or a -- in the data from changing the statement.

execute Flags

Choose exactly one target: --remote for a deployed SQLDB or --local for an isolated database on your machine. Local mode uses the runtime SQL engine in Docker.
The SQL text is submitted verbatim — the CLI never splits statements itself. A script may hold several ;-separated statements; all of them run in order, and the rows returned are the last statement’s rows. For --remote, the whole script must fit under the ~4 MiB request ceiling (see Limits). This request-size ceiling does not apply to --local --file. A database that has not finished provisioning rejects execute with 409 — Database is not ready (status: pending). Wait for provision_ok.

Local SQLDB development

Local SQLDB support is available in CLI v0.5.12 and newer. It requires Docker and the actor-runtime image used by telnyx-edge dev. Local databases are separate from deployed SQLDBs: running a local command never changes production data.
Use --local instead of --remote to execute SQL or list and apply migrations on your machine:
Local data persists under .telnyx/sqldb/ in the project directory and is excluded from Git. Delete that directory to reset local databases. Use --persist-to <directory> to store CLI local databases elsewhere; telnyx-edge dev only shares the default location. If telnyx.toml declares [storage.sqldb.DB], pass DB to local commands to use the same database as env.DB when running telnyx-edge dev. You do not need to provision a remote SQLDB to develop locally. --remote and --local are mutually exclusive; one is required for execute, migrations list, and migrations apply. Local commands run a short-lived Docker container and do not require a running dev stack.

Export

export dumps a database as a .sql file — schema, data, or both. Exports can be up to 4 GiB (CLI v0.5.15+). For restoring dumps larger than the approximately 4 MiB execute --remote --file request limit, use storage sqldb import (CLI v0.5.16+, up to 100 MiB).
The dump contains CREATE statements followed by INSERT statements, then indexes, views, and triggers. Values keep their exact storage class, including integers too large for a JSON number and BLOBs. The export runs as a job: the CLI queues it, polls until the dump is ready, then streams it to the file you named. The file is written to a temporary file in the destination directory and renamed into place on completion, so a failure never leaves a partial file wearing the final name. Files are created with mode 0600 — a dump is every row in plaintext. Databases containing virtual tables (for example FTS5) cannot be exported, because their contents live in shadow tables that do not round-trip. The export is not a point-in-time snapshot: a write committed while it runs may be partially reflected.

export Flags

--no-data and --no-schema cannot be combined (together they would export nothing).

Large SQLDB exports

As of CLI v0.5.15, storage sqldb export supports dumps up to 4 GiB. The existing --remote --output syntax is unchanged. Exports are produced through Cloud Storage rather than held entirely in memory. The execute --remote --file command still has an approximately 4 MiB SQL script limit. For larger restores, use the dedicated import command below. Note that the import ceiling is 100 MiB, lower than the 4 GiB export ceiling: not every export can be imported through the CLI.

Import SQL dumps

Available in telnyx-edge v0.5.16 and newer. Imports support SQL dump files up to 100 MiB.
Use storage sqldb import to restore a SQL dump through Telnyx Cloud Storage instead of sending the whole file through execute --remote --file:
The export command prints an export ID you can reuse with --from-export. An import into a nonempty database asks for confirmation; pass --yes to skip the prompt. This does not bypass the 100 MiB size limit. Validation and partial failures: The complete SQL dump is validated before application. The import then runs in batches. Each batch is atomic, but the overall import is not atomic: if a later batch fails, successfully applied earlier batches remain in the database. Review the detailed error and current database contents before retrying. Progress and cancellation: The CLI shows import progress. Pressing Ctrl-C stops the CLI from waiting, not the server-side import. Use the printed import status command to resume checking. The CLI also supports --json output for completed imports. Cloud Storage cleanup: For --file imports, the temporary uploaded object is removed on success. On failure it is retained and the CLI prints its bucket and key. Imports using --from-export do not delete the original export.

Migrations

Versioned schema changes live in numbered .sql files and are tracked in a sqldb_migrations table inside the database itself.

migrations Flags

The full workflow — file naming, the ledger table, and what happens when a migration fails midway — is covered in Migrations.

Generating Types

telnyx-edge types reads the manifest and writes telnyx-env.d.ts, so env.<BINDING> is typed at compile time. It runs fully offline — no authentication, no network.
Two things are checked before anything is written: the id must be a well-formed UUID (inline creation and binding by name are rejected), and @telnyx/edge-runtime in package.json must be 0.9.0 or newer — the first version that exports SqlDatabase. An older pin fails with @telnyx/edge-runtime <declared version> does not export SqlDatabase (requires >= 0.9.0), quoting the range exactly as package.json declares it.

Addressing a Database

For --remote, execute, migrations list, and migrations apply resolve the <database> argument against deployed SQLDBs:
  • An exact id match always wins.
  • A name resolves when exactly one database in the organization carries it.
  • A name shared by several databases errors with Multiple SQL databases are named "..." — pass the id instead.
  • No match errors with No SQL database found matching "...".
For --local, there is no organization lookup. A manifest binding name or that binding’s database ID opens the same local database used by telnyx-edge dev. Any other name creates or opens its own local-only database under the local persistence directory. Names are unique within an organization, so an ambiguous remote match should not occur in practice — the CLI guards against it anyway. get and delete do not resolve names; they take the id.
  • Quick Start — create a database, bind it, query it
  • Migrations — versioned schema changes and the sqldb_migrations ledger
  • Limits — request body size, query duration, and what is not enforced
  • SqlDatabase — the env binding surface
  • CLI Reference — every telnyx-edge command