Type generation
sqlfu generate reads checked-in .sql files and emits TypeScript wrappers under
sql/.generated/. The generated function name normally comes from the file path:
sql/get-post.sql becomes getPost.
-- sql/get-post.sqlselect id, slug, titlefrom postswhere id = :id;import {getPost} from './sql/.generated/get-post.sql';
const post = await getPost(client, {id: 123});Multiple queries in one file
Section titled “Multiple queries in one file”Put @name in a block comment before each query when one .sql file contains more
than one query.
/** @name listPosts */select id, slug, titlefrom postsorder by id;
/** @name findPostBySlug */select id, slug, titlefrom postswhere slug = :slug;This emits one generated module, sql/.generated/posts.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
Section titled “Parameter forms”Plain params use sqlfu’s normal :name placeholder syntax.
/** @name getPost */select id, slug, titlefrom postswhere id = :id;await getPost(client, {id: 123});Use IN (:ids) or NOT IN (:ids) when one scalar param should expand into a
comma-separated placeholder list. TypeSQL infers the array type from the IN
operator, and sqlfu uses that inferred type to expand the runtime placeholders.
/** @name listPostsByIds */select id, slug, titlefrom postswhere id in (:ids)order by id;await listPostsByIds(client, {ids: [1, 2, 3]});At runtime sqlfu executes where id in (?, ?, ?) with [1, 2, 3]. Empty arrays
throw before the query reaches SQLite.
Use dot paths when a query naturally accepts one object.
/** @name insertPost */insert into posts (slug, title)values (:post.slug, :post.title)returning id, slug, title;await insertPost(client, { post: { slug: 'hello-world', title: 'Hello world', },});The generated params type is {post: {slug: string; title: string}}. One object
path segment is supported today; nested paths such as :post.author.id are
intentionally rejected until the type shape is designed.
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.
/** @name insertPosts */insert into posts (slug, title)values :posts;await insertPosts(client, { posts: {slug: 'first', title: 'First'},});
await insertPosts(client, { posts: [ {slug: 'second', title: 'Second'}, {slug: 'third', title: 'Third'}, ],});At runtime sqlfu executes values (?, ?) for one object or values (?, ?), (?, ?)
for an array, and flattens values in the INSERT column-list order. Empty arrays
throw. This inferred INSERT 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 also infer object-array params from the left-hand column
tuple.
/** @name listPostsByKeys */select id, slug, titlefrom postswhere (slug, title) in (:keys)order by id;await listPostsByKeys(client, { keys: [ {slug: 'first', title: 'First'}, {slug: 'third', title: 'Third'}, ],});Limits
Section titled “Limits”- Runtime-expanded params, currently 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 the driver arguments, so sqlfu rejects that shape for now. - Typed JSON params are not supported yet. A JSON column is still usable as a regular scalar param, but sqlfu does not infer or enforce the TypeScript object shape inside SQLite JSON text/blob values.
- 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.