?{ } — the SQL context
SQL as a language context. No ORM, no query builder, no API layer. The server boundary disappears.
Syntax
?{ <sql template literal> }.<result-mode>()
The SQL goes inside ?{…}.
Bound parameters use template-literal interpolation
(${expr}).
The result-mode method on the end picks the result shape:
.all(),
.get(),
.run(),
.values().
Worked example
A four-call contact book sketch — INSERT, SELECT, DELETE — all inline in scrml. No API definition, no fetch wrapper, no server route to wire up. The compiler reads the SQL sites, infers which functions need to run server-side (per SPEC §12 route inference), and generates the wire format automatically.
function addContact(name, email) {
?{`INSERT INTO contacts (name, email) VALUES (...)`}.run()
}
function loadContacts() {
return ?{`SELECT id, name, email FROM contacts`}.all()
}
function deleteContact(id) {
?{`DELETE FROM contacts WHERE id = ...`}.run()
}
The full runnable example lives at examples/03-contact-book.scrml in the scrml repository. For a compiled example you can pull apart in the browser — real source beside the real emitted JS, HTML and CSS — see the showcase.
server keyword
above. A function containing
?{} is inferred as
server-side — you do not pin it by hand, and the
server modifier on a function is
deprecated in favour of that inference. Calls from
client-side markup auto-await and auto-fetch via the
compiler's wire format (SPEC §57). Parameter binding is
prepared-statement-safe — interpolations bind, they don't
string-concatenate.
Semantics
-
Bound parameters via interpolation, not concatenation.
${name}inside a SQL template literal becomes a bound parameter; the compiler emits a prepared statement with parameter binding. SQL injection is structurally prevented. -
Four result modes.
.all()returns an array of rows;.get()returns one row ornot(single-row T | not);.run()executes without returning rows (INSERT / UPDATE / DELETE);.values()returns columns as arrays. -
Per-handler transactions are implicit.
Multiple
?{}calls within a single event handler are coalesced into one transaction. Per SPEC §19.10.5. The N+1 loop pattern hoists automatically (SPEC §8.10). -
Route inference owns the wiring. Functions
containing
?{}are inferred as server-side; their call sites in client-rendered markup auto-await and auto-fetch via the generated wire endpoint (SPEC §12 + §57). The developer writes zero API routes, zero fetch wrappers, zero request/response types. -
Multi-database adaptation.
?{}lowers through Bun.SQL's driver layer (SPEC §44). Switch thedb=attribute, switch the database. SQLite, Postgres, MySQL. The?{}site is database-agnostic at the language level.
Errors this feature can fire
- E-SQL-006 — .prepare() removed (use template-string SQL directly)
- E-SQL-008 — bracket-matched ?{ scanner caught unmatched brace
- E-RI-002 — server-side ?{} mutation outside expected scope
Edge cases
-
.prepare()is REMOVED. The pre-v0.next.prepare()method fires E-SQL-006. Use the template-string form directly:?{`SELECT …`}.all(). -
The bracket-matched scanner allows nested
{inside SQL. The?{scanner per SPEC §44.8 (F-SQL-001) handles nested braces inside JSON-shaped SQL functions (jsonb_build_object,json_extract, etc.) without bailing. -
The server function boundary is the smallest unit
of "must run server-side."
You don't mark
?{}sites individually. You markserver functionsites; the route-inference pass then ensures?{}sites land inside server-side execution. -
lift+?{}.all()is the canonical "load this data" pattern. Theliftkeyword surfaces the return value to the enclosing expression context. In a server function body it propagates the rows back to the caller.
Related features
-
<db src=… tables=…>
— database declaration. Pairs with
?{}sites by lexical scope. - <schema> — SQL-mirror schema declarations. Owns table shape + migration diff.
- lift — value surfacing. The canonical "return rows" verb in server function bodies that load data for the caller.
- <program db=…> — app-level database default. Each route can override via <page db=…> .
- Learn: The server boundary disappears — narrative walkthrough of route inference + auto-await.
Availability
| Surface | Since | Notes |
|---|---|---|
| ?{} SQL context | v0.1.0 | SPEC §8; foundational from scrml8 era |
| Per-handler implicit transactions | SPEC §19.10.5 | Coalesces multiple ?{} calls in one event handler |
| N+1 loop hoist | SPEC §8.10 | Compiler hoists per-row ?{} sites out of loops |
| Bracket-matched scanner (F-SQL-001) | SPEC §44.8 | Allows nested {} inside JSON-shaped SQL functions |
| Multi-DB driver adaptation | SPEC §44 | SQLite / Postgres / MySQL via Bun.SQL |
Specification
This page summarizes SPEC §8 (~5364-5925) and cross-refs SPEC §12 (route inference), §19.10.5 (implicit transactions), §44 (multi-DB adaptation), §57 (wire format). The normative text lives at compiler/SPEC.md . When this page disagrees with SPEC.md, SPEC.md is authoritative.