Documentation Index
Fetch the complete documentation index at: https://mintlify.com/iterate/sqlfu/llms.txt
Use this file to discover all available pages before exploring further.
sqlfu generate reads checked-in .sql files and emits TypeScript wrappers into sql/.generated/. The generated function name comes from the file path: sql/get-post.sql becomes getPost. You write the data-access layer in plain SQL, then call it from TypeScript with inferred params and result rows — no query builder, no DSL, no rewriting SQL into another syntax.
sql/queries.sql
app/post.ts
File path to function name
The generated function name is derived from the file path.sql/get-post.sql becomes getPost. When one file contains multiple queries, use @name block comments to assign a name to each query.
Multiple queries in one file
Put@name in a block comment before each query when one .sql file contains more than one query.
sql/queries.sql
sql/.generated/queries.sql.ts, with both listPosts and findPostBySlug exports. If a file uses @name, every executable statement in that file must have its own @name.
Parameter forms
sqlfu infers the TypeScript parameter type from the SQL shape. The placeholder syntax you use in.sql files directly determines how runtime arguments are structured and how the SQL is expanded before execution.
Plain scalar params (:id)
The basic form. One placeholder maps to one scalar argument.
sql/queries.sql
Scalar lists (IN (:ids))
Use IN (:ids) or NOT IN (:ids) when one param should expand into a comma-separated placeholder list. TypeSQL infers the array type from the IN operator.
sql/queries.sql
where id in (?, ?, ?) with [1, 2, 3]. Empty arrays throw before the query reaches SQLite.
Dot paths (:post.slug)
Use dot paths when a query naturally accepts one object. One path segment is supported; nested paths such as :post.author.id are intentionally rejected until the type shape is designed.
sql/queries.sql
{post: {slug: string; title: string}}.
INSERT object shorthand (values :posts)
Use an object param directly after values when an INSERT column list already names the object fields. The generated param accepts either one object or a non-empty array.
sql/queries.sql
values (?, ?) for one object or values (?, ?), (?, ?) for an array, flattening values in INSERT column-list order. Empty arrays throw.
The INSERT object shorthand does not support
RETURNING yet. Use explicit dot-path values such as values (:post.slug, :post.title) for returning single-row inserts.Row-value IN lists ((slug, title) in (:keys))
Row-value IN lists infer object-array params from the left-hand column tuple.
sql/queries.sql
generate.authority
sqlfu generate needs to know your schema to produce typed query wrappers. The generate.authority option controls where it reads the schema from:
| Value | Source | DB required |
|---|---|---|
'desired_schema' (default) | Reads definitions.sql directly | No |
'migrations' | Replays migrations/*.sql into a scratch DB | No |
'migration_history' | Reads sqlfu_migrations from config.db, replays matching migrations | Yes |
'live_schema' | Extracts schema directly from config.db | Yes |
sqlfu.config.ts
'desired_schema' (the default) for the fastest, most deterministic output. Use 'migration_history' when generated types should match what is actually deployed. Drift between definitions.sql and migrations is surfaced by sqlfu check, not silently hidden by the generator.
Experimental JSON logical types
Setgenerate.experimentalJsonTypes: true to opt into experimental JSON logical-type handling. Columns declared with the SQLite type name json are generated as unknown by default. For a narrower TypeScript type, add a sqlfu_types metadata view to your schema. Each row maps a logical declared type name to an encoding, a definition format, and a type definition:
definitions.sql
definition, serializes inputs as JSON text, and parses selected result columns before returning them.
The
definition value is not a validator schema and sqlfu does not resolve imports, aliases, or references from it. The first experimental slice supports only encoding: 'json' and format: 'typescript'.Effect SQL runtime
If your app uses Effect, setgenerate.runtime: 'effect-v3' to emit functions that return Effect values and require Effect SQL’s SqlClient.SqlClient from the Effect environment:
'effect-v4-unstable' target.
Limits
- Runtime-expanded params (inferred scalar
INlists, row-valueINlists, and INSERTvalues :paramobjects) can appear only once in a query. Reusing the same expanded array in two places would require duplicating driver arguments, so sqlfu rejects that shape. generate.experimentalJsonTypesmust be opted into explicitly. JSON logical-type handling — including columns declared asjsonand matching rows insqlfu_types— is not active by default.sqlfu_types.definitionvalues only describe the generated TypeScript surface. They do not add runtime validation for JSON payload shape.- Parameter shape is inferred from SQL shape, not comment metadata.
@namenames queries;IN (:ids),(slug, title) in (:keys), andvalues :postsdescribe runtime placeholder expansion where the SQL shape changes. - One dot-path segment is supported (
:post.slug). Nested paths such as:post.author.idare rejected.
Related pages
Runtime validation
Validate params on input and rows on output with arktype, valibot, zod, or zod-mini.
Observability
Route query names to OpenTelemetry spans, Sentry errors, and Datadog metrics.
Adapters
Bring your own SQLite driver: better-sqlite3, node:sqlite, Cloudflare D1, and more.
Lint plugin
Keep generated wrappers fresh and enforce SQL-first conventions in ESLint.