scrml.dev v0.7.1
Reference › Contexts

?{ } — 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.

Note there is no 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 or not (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 the db= 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 mark server function sites; the route-inference pass then ensures ?{} sites land inside server-side execution.
  • lift + ?{}.all() is the canonical "load this data" pattern. The lift keyword surfaces the return value to the enclosing expression context. In a server function body it propagates the rows back to the caller.

Related features

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.