Validators
Validator-integration fixtures: zod, valibot, zod/mini, arktype. Runtime-behaviour tests
(actually importing the transpiled module and calling it) stay in generate.test.ts — the
fixtures here just pin the emitted source shape. We also cover prettyErrors: false for each,
which changes the emitted file significantly (inline issue-throwing instead of the shared
runtime helper), and the plain-TS default when no validator is configured.
Most tests on this page override sqlfu.config.ts to set the generate.validator they care
about; the default below is just the plain-TS baseline used by the last test.
default config
export default { db: './app.db', migrations: './migrations', definitions: './definitions.sql', queries: './sql',};validator: zod emits zod schemas with namespace-merged exports
Section titled “validator: zod emits zod schemas with namespace-merged exports”input
create table posts ( id integer primary key, slug text not null, title text, status text not null check (status in ('draft', 'published')));export default { db: './app.db', migrations: './migrations', definitions: './definitions.sql', queries: './sql', generate: {validator: 'zod'},};select id, slug, title, status from posts where slug = :slug limit 1;output
import type {Client} from 'sqlfu';import {z} from 'zod';
const Params = z.object({ slug: z.string(),});const Result = z.object({ id: z.number(), slug: z.string(), title: z.string().nullable(), status: z.enum(["draft", "published"]),});const sql = `select id, slug, title, status from posts where slug = ? limit 1;`;const query = (params: findPostBySlug.Params) => ({ sql, args: [params.slug], name: "findPostBySlug" });
export const findPostBySlug = Object.assign( async function findPostBySlug(client: Client, params: findPostBySlug.Params): Promise<findPostBySlug.Result | null> { const parsedParams = Params.safeParse(params); if (!parsedParams.success) throw new Error(z.prettifyError(parsedParams.error)); const rows = await client.all(query(parsedParams.data)); if (rows.length === 0) return null; const parsed = Result.safeParse(rows[0]); if (!parsed.success) throw new Error(z.prettifyError(parsed.error)); return parsed.data; }, { Params, Result, sql, query },);
export namespace findPostBySlug { export type Params = z.infer<typeof findPostBySlug.Params>; export type Result = z.infer<typeof findPostBySlug.Result>;}export * from "./tables.js";export * from "./find-post-by-slug.sql.js";// Generated by `sqlfu generate`. Do not edit.// Row types for every table and view in your project's schema.
export type PostsRow = { id: number; slug: string; title: string | null; status: string;};validator: valibot emits valibot schemas
Section titled “validator: valibot emits valibot schemas”input
create table posts ( id integer primary key, slug text not null, title text, status text not null check (status in ('draft', 'published')));export default { db: './app.db', migrations: './migrations', definitions: './definitions.sql', queries: './sql', generate: {validator: 'valibot'},};select id, slug, title, status from posts where slug = :slug limit 1;output
import {type Client, prettifyStandardSchemaError} from 'sqlfu';import * as v from 'valibot';
const Params = v.object({ slug: v.string(),});const Result = v.object({ id: v.number(), slug: v.string(), title: v.nullable(v.string()), status: v.picklist(["draft", "published"]),});const sql = `select id, slug, title, status from posts where slug = ? limit 1;`;const query = (params: findPostBySlug.Params) => ({ sql, args: [params.slug], name: "findPostBySlug" });
export const findPostBySlug = Object.assign( async function findPostBySlug(client: Client, params: findPostBySlug.Params): Promise<findPostBySlug.Result | null> { const parsedParamsResult = Params['~standard'].validate(params); if ('then' in parsedParamsResult) throw new Error('Unexpected async validation from Params.'); if ('issues' in parsedParamsResult) throw new Error(prettifyStandardSchemaError(parsedParamsResult) || 'Validation failed'); const rows = await client.all(query(parsedParamsResult.value)); if (rows.length === 0) return null; const parsed = Result['~standard'].validate(rows[0]); if ('then' in parsed) throw new Error('Unexpected async validation from Result.'); if ('issues' in parsed) throw new Error(prettifyStandardSchemaError(parsed) || 'Validation failed'); return parsed.value; }, { Params, Result, sql, query },);
export namespace findPostBySlug { export type Params = v.InferOutput<typeof findPostBySlug.Params>; export type Result = v.InferOutput<typeof findPostBySlug.Result>;}export * from "./tables.js";export * from "./find-post-by-slug.sql.js";// Generated by `sqlfu generate`. Do not edit.// Row types for every table and view in your project's schema.
export type PostsRow = { id: number; slug: string; title: string | null; status: string;};validator: zod-mini emits zod/mini schemas
Section titled “validator: zod-mini emits zod/mini schemas”input
create table posts (id integer primary key, slug text not null, title text);export default { db: './app.db', migrations: './migrations', definitions: './definitions.sql', queries: './sql', generate: {validator: 'zod-mini'},};select id, slug, title from posts where slug = :slug limit 1;output
import {type Client, prettifyStandardSchemaError} from 'sqlfu';import * as z from 'zod/mini';
const Params = z.object({ slug: z.string(),});const Result = z.object({ id: z.number(), slug: z.string(), title: z.nullable(z.string()),});const sql = `select id, slug, title from posts where slug = ? limit 1;`;const query = (params: findPostBySlug.Params) => ({ sql, args: [params.slug], name: "findPostBySlug" });
export const findPostBySlug = Object.assign( async function findPostBySlug(client: Client, params: findPostBySlug.Params): Promise<findPostBySlug.Result | null> { const parsedParamsResult = Params['~standard'].validate(params); if ('then' in parsedParamsResult) throw new Error('Unexpected async validation from Params.'); if ('issues' in parsedParamsResult) throw new Error(prettifyStandardSchemaError(parsedParamsResult) || 'Validation failed'); const rows = await client.all(query(parsedParamsResult.value)); if (rows.length === 0) return null; const parsed = Result['~standard'].validate(rows[0]); if ('then' in parsed) throw new Error('Unexpected async validation from Result.'); if ('issues' in parsed) throw new Error(prettifyStandardSchemaError(parsed) || 'Validation failed'); return parsed.value; }, { Params, Result, sql, query },);
export namespace findPostBySlug { export type Params = z.infer<typeof findPostBySlug.Params>; export type Result = z.infer<typeof findPostBySlug.Result>;}export * from "./tables.js";export * from "./find-post-by-slug.sql.js";// Generated by `sqlfu generate`. Do not edit.// Row types for every table and view in your project's schema.
export type PostsRow = { id: number; slug: string; title: string | null;};validator: zod + prettyErrors: false drops the prettify helper and safeParse wrapper
Section titled “validator: zod + prettyErrors: false drops the prettify helper and safeParse wrapper”input
create table posts (id integer primary key, slug text not null);export default { db: './app.db', migrations: './migrations', definitions: './definitions.sql', queries: './sql', generate: {validator: 'zod', prettyErrors: false},};select id, slug from posts where slug = :slug limit 1;output
import type {Client} from 'sqlfu';import {z} from 'zod';
const Params = z.object({ slug: z.string(),});const Result = z.object({ id: z.number(), slug: z.string(),});const sql = `select id, slug from posts where slug = ? limit 1;`;const query = (params: findPostBySlug.Params) => ({ sql, args: [params.slug], name: "findPostBySlug" });
export const findPostBySlug = Object.assign( async function findPostBySlug(client: Client, params: findPostBySlug.Params): Promise<findPostBySlug.Result | null> { const rows = await client.all(query(Params.parse(params))); return rows.length > 0 ? Result.parse(rows[0]) : null; }, { Params, Result, sql, query },);
export namespace findPostBySlug { export type Params = z.infer<typeof findPostBySlug.Params>; export type Result = z.infer<typeof findPostBySlug.Result>;}export * from "./tables.js";export * from "./find-post-by-slug.sql.js";// Generated by `sqlfu generate`. Do not edit.// Row types for every table and view in your project's schema.
export type PostsRow = { id: number; slug: string;};validator: valibot + prettyErrors: false throws raw issues inline
Section titled “validator: valibot + prettyErrors: false throws raw issues inline”input
create table posts (id integer primary key, slug text not null);export default { db: './app.db', migrations: './migrations', definitions: './definitions.sql', queries: './sql', generate: {validator: 'valibot', prettyErrors: false},};select id, slug from posts where slug = :slug limit 1;output
import type {Client} from 'sqlfu';import * as v from 'valibot';
const Params = v.object({ slug: v.string(),});const Result = v.object({ id: v.number(), slug: v.string(),});const sql = `select id, slug from posts where slug = ? limit 1;`;const query = (params: findPostBySlug.Params) => ({ sql, args: [params.slug], name: "findPostBySlug" });
export const findPostBySlug = Object.assign( async function findPostBySlug(client: Client, params: findPostBySlug.Params): Promise<findPostBySlug.Result | null> { const parsedParamsResult = Params['~standard'].validate(params); if ('then' in parsedParamsResult) throw new Error('Unexpected async validation from Params.'); if ('issues' in parsedParamsResult) throw Object.assign(new Error('Validation failed'), {issues: parsedParamsResult.issues}); const rows = await client.all(query(parsedParamsResult.value)); if (rows.length === 0) return null; const parsed = Result['~standard'].validate(rows[0]); if ('then' in parsed) throw new Error('Unexpected async validation from Result.'); if ('issues' in parsed) throw Object.assign(new Error('Validation failed'), {issues: parsed.issues}); return parsed.value; }, { Params, Result, sql, query },);
export namespace findPostBySlug { export type Params = v.InferOutput<typeof findPostBySlug.Params>; export type Result = v.InferOutput<typeof findPostBySlug.Result>;}export * from "./tables.js";export * from "./find-post-by-slug.sql.js";// Generated by `sqlfu generate`. Do not edit.// Row types for every table and view in your project's schema.
export type PostsRow = { id: number; slug: string;};validator: zod-mini + prettyErrors: false throws raw issues inline
Section titled “validator: zod-mini + prettyErrors: false throws raw issues inline”input
create table posts (id integer primary key, slug text not null);export default { db: './app.db', migrations: './migrations', definitions: './definitions.sql', queries: './sql', generate: {validator: 'zod-mini', prettyErrors: false},};select id, slug from posts where slug = :slug limit 1;output
import type {Client} from 'sqlfu';import * as z from 'zod/mini';
const Params = z.object({ slug: z.string(),});const Result = z.object({ id: z.number(), slug: z.string(),});const sql = `select id, slug from posts where slug = ? limit 1;`;const query = (params: findPostBySlug.Params) => ({ sql, args: [params.slug], name: "findPostBySlug" });
export const findPostBySlug = Object.assign( async function findPostBySlug(client: Client, params: findPostBySlug.Params): Promise<findPostBySlug.Result | null> { const parsedParamsResult = Params['~standard'].validate(params); if ('then' in parsedParamsResult) throw new Error('Unexpected async validation from Params.'); if ('issues' in parsedParamsResult) throw Object.assign(new Error('Validation failed'), {issues: parsedParamsResult.issues}); const rows = await client.all(query(parsedParamsResult.value)); if (rows.length === 0) return null; const parsed = Result['~standard'].validate(rows[0]); if ('then' in parsed) throw new Error('Unexpected async validation from Result.'); if ('issues' in parsed) throw Object.assign(new Error('Validation failed'), {issues: parsed.issues}); return parsed.value; }, { Params, Result, sql, query },);
export namespace findPostBySlug { export type Params = z.infer<typeof findPostBySlug.Params>; export type Result = z.infer<typeof findPostBySlug.Result>;}export * from "./tables.js";export * from "./find-post-by-slug.sql.js";// Generated by `sqlfu generate`. Do not edit.// Row types for every table and view in your project's schema.
export type PostsRow = { id: number; slug: string;};validator: arktype emits arktype schemas
Section titled “validator: arktype emits arktype schemas”input
create table posts ( id integer primary key, slug text not null, title text, status text not null check (status in ('draft', 'published')));export default { db: './app.db', migrations: './migrations', definitions: './definitions.sql', queries: './sql', generate: {validator: 'arktype'},};select id, slug, title, status from posts where slug = :slug limit 1;output
import {type Client, prettifyStandardSchemaError} from 'sqlfu';import {type} from 'arktype';
const Params = type({ slug: "string",});const Result = type({ id: "number", slug: "string", title: "string | null", status: "\"draft\" | \"published\"",});const sql = `select id, slug, title, status from posts where slug = ? limit 1;`;const query = (params: findPostBySlug.Params) => ({ sql, args: [params.slug], name: "findPostBySlug" });
export const findPostBySlug = Object.assign( async function findPostBySlug(client: Client, params: findPostBySlug.Params): Promise<findPostBySlug.Result | null> { const parsedParamsResult = Params['~standard'].validate(params); if ('then' in parsedParamsResult) throw new Error('Unexpected async validation from Params.'); if ('issues' in parsedParamsResult) throw new Error(prettifyStandardSchemaError(parsedParamsResult) || 'Validation failed'); const rows = await client.all(query(parsedParamsResult.value)); if (rows.length === 0) return null; const parsed = Result['~standard'].validate(rows[0]); if ('then' in parsed) throw new Error('Unexpected async validation from Result.'); if ('issues' in parsed) throw new Error(prettifyStandardSchemaError(parsed) || 'Validation failed'); return parsed.value; }, { Params, Result, sql, query },);
export namespace findPostBySlug { export type Params = typeof findPostBySlug.Params.infer; export type Result = typeof findPostBySlug.Result.infer;}export * from "./tables.js";export * from "./find-post-by-slug.sql.js";// Generated by `sqlfu generate`. Do not edit.// Row types for every table and view in your project's schema.
export type PostsRow = { id: number; slug: string; title: string | null; status: string;};validator: arktype + prettyErrors: false throws raw issues inline
Section titled “validator: arktype + prettyErrors: false throws raw issues inline”input
create table posts (id integer primary key, slug text not null);export default { db: './app.db', migrations: './migrations', definitions: './definitions.sql', queries: './sql', generate: {validator: 'arktype', prettyErrors: false},};select id, slug from posts where slug = :slug limit 1;output
import type {Client} from 'sqlfu';import {type} from 'arktype';
const Params = type({ slug: "string",});const Result = type({ id: "number", slug: "string",});const sql = `select id, slug from posts where slug = ? limit 1;`;const query = (params: findPostBySlug.Params) => ({ sql, args: [params.slug], name: "findPostBySlug" });
export const findPostBySlug = Object.assign( async function findPostBySlug(client: Client, params: findPostBySlug.Params): Promise<findPostBySlug.Result | null> { const parsedParamsResult = Params['~standard'].validate(params); if ('then' in parsedParamsResult) throw new Error('Unexpected async validation from Params.'); if ('issues' in parsedParamsResult) throw Object.assign(new Error('Validation failed'), {issues: parsedParamsResult.issues}); const rows = await client.all(query(parsedParamsResult.value)); if (rows.length === 0) return null; const parsed = Result['~standard'].validate(rows[0]); if ('then' in parsed) throw new Error('Unexpected async validation from Result.'); if ('issues' in parsed) throw Object.assign(new Error('Validation failed'), {issues: parsed.issues}); return parsed.value; }, { Params, Result, sql, query },);
export namespace findPostBySlug { export type Params = typeof findPostBySlug.Params.infer; export type Result = typeof findPostBySlug.Result.infer;}export * from "./tables.js";export * from "./find-post-by-slug.sql.js";// Generated by `sqlfu generate`. Do not edit.// Row types for every table and view in your project's schema.
export type PostsRow = { id: number; slug: string;};validator: valibot emits a Result schema with no Params when the query takes no args
Section titled “validator: valibot emits a Result schema with no Params when the query takes no args”input
create table posts (id integer primary key, slug text not null, title text);export default { db: './app.db', migrations: './migrations', definitions: './definitions.sql', queries: './sql', generate: {validator: 'valibot'},};select id, slug, title from posts limit 1;output
import {type Client, prettifyStandardSchemaError} from 'sqlfu';import * as v from 'valibot';
const Result = v.object({ id: v.number(), slug: v.string(), title: v.nullable(v.string()),});const sql = `select id, slug, title from posts limit 1;`;const query = { sql, args: [], name: "latestPost" };
export const latestPost = Object.assign( async function latestPost(client: Client): Promise<latestPost.Result | null> { const rows = await client.all(query); if (rows.length === 0) return null; const parsed = Result['~standard'].validate(rows[0]); if ('then' in parsed) throw new Error('Unexpected async validation from Result.'); if ('issues' in parsed) throw new Error(prettifyStandardSchemaError(parsed) || 'Validation failed'); return parsed.value; }, { Result, sql, query },);
export namespace latestPost { export type Result = v.InferOutput<typeof latestPost.Result>;}validator: zod + insert metadata skips result validation but keeps Params
Section titled “validator: zod + insert metadata skips result validation but keeps Params”input
create table posts (id integer primary key, slug text not null);export default { db: './app.db', migrations: './migrations', definitions: './definitions.sql', queries: './sql', generate: {validator: 'zod'},};insert into posts (slug) values (:slug);output
import type {Client} from 'sqlfu';import {z} from 'zod';
const Params = z.object({ slug: z.string(),});const sql = `insert into posts (slug) values (?);`;const query = (params: insertPost.Params) => ({ sql, args: [params.slug], name: "insertPost" });
export const insertPost = Object.assign( async function insertPost(client: Client, params: insertPost.Params) { const parsedParams = Params.safeParse(params); if (!parsedParams.success) throw new Error(z.prettifyError(parsedParams.error)); return client.run(query(parsedParams.data)); }, { Params, sql, query },);
export namespace insertPost { export type Params = z.infer<typeof insertPost.Params>;}export * from "./tables.js";export * from "./insert-post.sql.js";// Generated by `sqlfu generate`. Do not edit.// Row types for every table and view in your project's schema.
export type PostsRow = { id: number; slug: string;};no validator: plain-TS output matches the default wrapper shape
Section titled “no validator: plain-TS output matches the default wrapper shape”input
create table posts (id integer primary key, slug text not null);select id, slug from posts;output
import type {Client} from 'sqlfu';
const sql = `select id, slug from posts;`;const query = { sql, args: [], name: "listPosts" };
export const listPosts = Object.assign( async function listPosts(client: Client): Promise<listPosts.Result[]> { return client.all<listPosts.Result>(query); }, { sql, query },);
export namespace listPosts { export type Result = { id: number; slug: string; };}export * from "./tables.js";export * from "./list-posts.sql.js";// Generated by `sqlfu generate`. Do not edit.// Row types for every table and view in your project's schema.
export type PostsRow = { id: number; slug: string;};