SQL file imports
Each .sql file contains one statement and exports a query factory returning the same Query<Row, Parameters> accepted by database.all, one, and maybeOne.
Stable build and test workflow
Use @typed-sql/build with your bundler or test runner. Generate co-located .d.sql.ts declarations with typed-sql-build types and enable allowArbitraryExtensions in your TypeScript configuration. No TypeScript beta is required. Run generation before checking and use typed-sql-build types --check to verify committed outputs in CI. Build adapters validate the current SQL and attach checked runtime value plans.
The installation tutorials cover Vite/Vitest, Jest, webpack, Rspack, Rsbuild, Next.js, esbuild, Rollup, Rolldown, Bun, native Node test runners, Astro, Nuxt, SvelteKit, and Wrangler/D1.
Experimental live content mapper
The @typed-sql/ts-bridge content mapper checks .sql imports directly against the configured snapshot, without a declaration-generation step. It is tested with TypeScript 7.1.0-beta. TypeScript 7.0 and older do not support this content mapper. Use this optional frontend for live TypeScript checking of SQL assets; the stable build integrations supply runtime compilation.
Configure checking
Install the bridge and TypeScript beta alongside your selected grammar:
pnpm add @typed-sql/postgres @typed-sql/ts-bridge
pnpm add -D typescript@betaKeep your normal typed-sql configuration, including dialect, schema.file, and outDir. Generate the snapshot with your normal introspection workflow before checking SQL files. The mapper reads that snapshot; it does not call the configured provider or connect to a database.
Add the mapper to tsconfig.json:
{
"compilerOptions": {
"strict": true,
"noEmit": true,
"target": "ES2024",
"module": "NodeNext"
},
"contentMappers": [
{
"package": "@typed-sql/ts-bridge",
"extensions": [".sql"]
}
],
"include": ["src"]
}If the typed-sql config is elsewhere, set the mapper's options.config path relative to the TypeScript project directory. Each project uses one typed-sql configuration and one grammar.
Run the native compiler with explicit permission to execute the mapper:
pnpm exec tsc --runExternalCode
pnpm exec tsc --runExternalCode --watchnoEmit: true is required. This frontend supplies typechecking, not JavaScript or declaration emit. An emit-enabled mapper project reports an error. The native watch process observes the schema, typed-sql configuration, and its imported local configuration modules.
The experimental native TypeScript watcher can miss rapid edits on macOS while it changes directory subscriptions. SQL declarations retain their type dependencies across SQL errors to reduce that churn. If watch diagnostics appear stale, run tsc --runExternalCode without --watch to check the current files.
Try SQL parameter bindings in the playground, or follow the SQL files and application identities tutorial for a provider configuration, branded query arguments, and deployment artifacts.
Bind positional parameters
Write src/find-user.sql:
SELECT id, email
FROM users
WHERE id = $1 AND email <> $2Import and bind it from TypeScript:
import findUser from "./find-user.sql";
const query = findUser(1n, "excluded@example.com");
const rows = await database.all(query);The configured grammar and snapshot determine the parameter tuple and result row. In this example, a bigint id and text email produce readonly [bigint, string]. A file without placeholders exports a zero-argument factory. Incorrect argument types or counts are TypeScript errors.
PostgreSQL uses $1, $2, and so on. MySQL and SQLite use ? in binding order. Numbered placeholders can repeat or appear in a different order; arguments remain in numeric index order. Indexes must be contiguous from 1. The compiler expands repeated values into independent bound occurrences and rejects incompatible inferred types for the same argument.
Bind named parameters
All three grammars accept named parameters in SQL assets:
SELECT id, email FROM users WHERE id = :id OR parent_id = :idconst query = findUser({ id: userId });Names use ASCII letters, digits and underscores, starting with a letter or underscore. The factory takes an object containing exactly the declared own properties. Named and positional parameters cannot be mixed. Strings, quoted identifiers, comments and PostgreSQL casts retain their ordinary meaning. In PostgreSQL subscripts, parenthesize named bindings, as items[(:index)], to distinguish them from native slice bounds such as items[:upper]. MySQL scanning respects the snapshot's SQL mode. Structural interpolation remains explicit through inline composition.
SQL errors are reported against the original .sql file. Invalid SQL does not expose a callable typed factory and the runtime loader rejects the module. The runtime binds values through sql.value; user values never become structural SQL. Configure the database adapter with the same type policy used for checking, just as with inline queries.
Run on Node
Install @typed-sql/build as an application dependency for this runtime path. It requires Node.js 24.11.1 or newer; Node.js 25 requires 25.1.0 or newer. Start the ESM application from its typed-sql configuration directory:
node --import @typed-sql/build/register src/main.tsThe preload evaluates one configuration from the current working directory, reads its snapshot, and installs Node's synchronous module hooks. Node's built-in type stripping runs the example's TypeScript entrypoint directly; its normal restrictions on erasable TypeScript syntax apply. Restart the process after runtime configuration or snapshot changes. The compiler's watch refresh and Node's module cache are separate lifecycles.
Deploy the original .sql assets at the paths imported by your JavaScript, together with the typed-sql configuration, its local dependencies, and the snapshot. For example, an emitted dist/main.js importing ./find-user.sql needs dist/find-user.sql. Your application build must copy these assets; the mapper does not copy them. A Vite build can instead compile SQL into the application bundle.
Build for Workers and other bundled runtimes
Use the Wrangler custom build or an explicit bundler adapter when the deployed runtime cannot load configuration or SQL assets. Import the grammar's portable query entrypoint in runtime modules and keep the build integration in build configuration. Bundles contain static SQL and checked value plans; configuration and snapshots remain build inputs.
Each SQL module exports valuePlan; its typePolicyHash can configure a matching portable adapter profile. For inline queries, use getQueryValuePlan(query) from @typed-sql/core.
The adapters support bounded conditional fragments and grammar-proven repeated fragments. Result-dependent structures remain conservative when the compiler cannot prove an exact contract. Empty or changed fragment structures fail closed. Build transforms do not replace application typechecking: use typed-sql check for inline inference, plus generated SQL file declarations or the experimental content mapper for imported assets.
Editor and tooling boundaries
TypeScript 7.1's official editor integration runs configured content mappers in trusted workspaces after mapper permissions are granted. Enable the TypeScript 7 integration and use the beta SDK; see Microsoft's content mapper announcement. The same mapper reports SQL diagnostics and supplies the imported factory's TypeScript type. The typed-sql SDK proxy preserves the official client's external-code execution permission. Without that permission, mapped imports remain unavailable; the proxy does not enable external execution by default. SQL completion, rename, and definition mapping are not provided by this frontend.
The stable typed-sql check command checks inline TypeScript SQL templates. Generate SQL-file declarations with typed-sql-build types before ordinary checking, or use tsc --runExternalCode for live asset checking through the experimental mapper.
Include imported queries in artifacts
Query manifests discover literal relative or absolute .sql imports and re-exports in the configured TypeScript source graph. Unrelated SQL files, including migration scripts, are not collected. Package aliases and computed module paths are not SQL asset discovery routes. A missing imported SQL file fails discovery. The manifest records source hashes, original SQL-file locations, inferred contracts and dependencies; it does not persist SQL text or bound values.
Imported SQL statements participate in live verification, query-plan capture and schema compatibility reports using the same analysis as their runtime factories. Editing an SQL asset invalidates its manifest and cached proofs. Regenerate the manifest before verification or plan capture. Direct compiler API callers include SQL files in their sources array.