DB block
The DB block is a fenced markdown code block whose language is
db-<connection-name>, where <connection-name> is a key from
connections.toml. Each connection auto-registers its own block
type.
Minimal example
Section titled “Minimal example”```db-localSELECT id, name FROM users LIMIT 10```The fence language is db-local because there’s a [connections.local]
entry in connections.toml.
Supported drivers
Section titled “Supported drivers”| Type | Connection string | Notes |
|---|---|---|
sqlite | path = "./file.sqlite" | Auto-creates the file on first use |
postgres | host, port, database, user, password, ssl_mode | sqlx postgres driver |
mysql | host, port, database, user, password | sqlx mysql driver |
See config-files for the full
connections.toml schema.
Info-string tokens
Section titled “Info-string tokens”| Token | Default | Notes |
|---|---|---|
alias=<name> | none | Required for capture/chain ({{users.row[0].id}}). |
timeout=<ms> | 60000 | Query timeout in milliseconds. |
display=<mode> | split | Panel layout (same as HTTP). |
The fence language is the connection — there’s no separate
connection= token.
Bind parameters (SQL safety)
Section titled “Bind parameters (SQL safety)”{{...}} references in SQL body are always converted to bind
parameters. They are never string-interpolated.
```db-localSELECT * FROM ordersWHERE customer_id = {{user.body.id}} AND status = {{filter.body.status}}```What the driver sees:
SELECT * FROM ordersWHERE customer_id = $1 AND status = $2Bind values: [user.body.id, filter.body.status].
Zero SQL injection surface. Even with user-controlled values flowing in from HTTP responses, the driver’s prepared-statement binding handles type coercion and escaping.
Mutation guard
Section titled “Mutation guard”When a block contains DELETE, UPDATE, INSERT, TRUNCATE,
DROP, or ALTER, httui shows a confirmation banner before
running:
⚠ This block contains
UPDATE. Run anyway?
The guard is structural — httui parses the SQL with a scanner, not a regex on the first word. So:
UPDATEinside a--comment is not flagged.- A
DELETEburied after aSELECTin a multi-statement block is flagged.
Disable per-block via the toolbar gear → Skip mutation prompt (persisted per-block in the info-string). Best for runbooks you trust and run often.
Multi-statement blocks
Section titled “Multi-statement blocks”You can put multiple SQL statements in one block, separated by semicolons:
```db-localCREATE TEMP TABLE staging AS SELECT * FROM users WHERE active = false;DELETE FROM staging WHERE created_at < unixepoch() - 30 * 86400;SELECT count(*) FROM staging;```The mutation guard flags if any statement is destructive.
Results come from the last statement only — the result grid
shows the final SELECT’s rows.
Result panel
Section titled “Result panel”The response panel has a virtualised grid for SELECT results:
- Sortable column headers (click)
- Type badge per column (
int,text,jsonb, etc) - Copy-as-CSV button
- Windowed rendering — 100k+ rows stay scrollable
For mutation blocks (INSERT/UPDATE/DELETE), the panel shows
“N rows affected” + the elapsed time.
Chained references
Section titled “Chained references”The first row of every result is the implicit “response” — chain into it from later blocks:
```db-local alias=users_listSELECT id, email FROM users ORDER BY id LIMIT 1```
```httpGET {{BASE_URL}}/users/{{users_list.row[0].id}}```Available fields on a DB block response:
| Field | Meaning |
|---|---|
row[N].<col> | nth row’s column value |
rows | full result array (rare to chain on) |
rows.length | number of rows |
affected | rows affected by mutation |
EXPLAIN
Section titled “EXPLAIN”Run any EXPLAIN ANALYZE (Postgres / MySQL) or EXPLAIN QUERY PLAN
(SQLite) and the panel renders the plan as a tree view:
- Sequential scans with high cost are highlighted red
- Nested-loop joins with bad cardinality estimates are flagged
- Right-click any node → Copy as TEXT for sharing
Session override (host/port)
Section titled “Session override (host/port)”For connections where you need to SSH-tunnel locally but the shared
connections.toml points at the production hostname, use the
toolbar ⚙ Session override dialog:
host: 127.0.0.1port: 15432Override is session-only — not persisted to disk, applied per
DB run. Base connection pool is untouched; httui creates an
override-keyed pool for the override session. Useful for one-off
tunnel work without editing .local.toml.
Same SHA-256 hash as HTTP blocks:
SQL text (after bind substitution) + bind values + env snapshot (only referenced vars)Mutation statements (INSERT/UPDATE/DELETE/TRUNCATE/DROP/ALTER)
are never cached — they always re-execute. SELECT results cache;
clear via toolbar gear → Clear cache.
Schema explorer
Section titled “Schema explorer”Click the Schema tab in the sidebar to browse the active connection’s schema — tables, columns with types, foreign keys, indexes, row count estimates.
Schemas are introspected on connection open + cached in notes.db.
Click the refresh icon to re-introspect (useful after a migration).
Schema-aware autocomplete fires inside DB blocks: type a table name, get column suggestions; type a column name, get type info.
Keyboard
Section titled “Keyboard”| Shortcut | Action |
|---|---|
Cmd+Enter | Run block |
Cmd+. | Cancel running query |
Cmd+Shift+E | Run as EXPLAIN (wraps the query) |
Common errors
Section titled “Common errors”| Message | Cause | Fix |
|---|---|---|
connection refused | DB not reachable | Check host/port, tunnel, firewall |
relation "X" does not exist | Wrong schema / search_path | Use fully-qualified name public.X |
password authentication failed | Wrong creds | Update via Environment manager (see secrets) |
pool exhausted | Too many concurrent queries against same conn | Reduce parallelism in Run-all |
Related
Section titled “Related”- Add a database tutorial — first-time setup
- Block references —
{{row[N].col}} - Config files → connections.toml