GoodiesSupported
Small things that are easy to miss.
Get the SQL without a database#
Every builder ends in .compile(), which returns { text, parameters }. No connection, no mocking:
import { createQueryCompiler, trustedTable } from '@zmdb/sql';
import { postgres } from '@zmdb/postgres';
const { text, parameters } = createQueryCompiler(postgres).selectFrom(trustedTable('users')).where('age', '>=', 18).orderBy('email', 'asc').limit(10).compile();Assert on text in a unit test. This is the same value the driver gets.
Compile the same query for six dialects#
import { trustedTable } from '@zmdb/sql';
for (const dialect of ['postgres', 'mysql', 'sqlite', 'mssql', 'cockroach', 'singlestore'] as const) {
console.log(createQueryCompiler(dialect).selectFrom(trustedTable('users')).where('id', '=', 1).compile().text);
}
// SELECT * FROM "users" WHERE "id" = $1
// SELECT * FROM `users` WHERE `id` = ?
// SELECT * FROM "users" WHERE "id" = ?
// SELECT * FROM [users] WHERE [id] = @p1
// SELECT * FROM "users" WHERE "id" = $1
// SELECT * FROM `users` WHERE `id` = ?Useful for spotting portability problems before deploy, and for tests that must pass on both SQLite locally and Postgres in CI.
Generate realistic fixtures from the schema#
import { seedRows } from '@zmdb/orm/seeding';
const rows = seedRows(userSchema, { count: 50, seed: 1234 });Deterministic for a given seed, and shaped by the column's whole declaration rather than only its SQL type — a literal union gets a member, a timestamp gets a Date, a Min<18> column gets a number at least eighteen. It is the same sampler random<T>() uses, so the rows satisfy repo.create's own validator. See Seed Value Generators.
Generate a value from any type, not just a schema#
import { random } from '@zmdb/validator';
const u = random<User>();The transformer derives the generator from User itself. Handy for property-based tests. See Random Generator.
stringify is faster than JSON.stringify for known types#
import { stringify, assertStringify } from '@zmdb/validator/serialization';
stringify(user); // no key discovery at runtime
assertStringify<User>(user); // validate, then serializeThe transformer knows the key set, so there is no Object.keys walk and no property-order surprise. See stringify().
parse returns a result, it does not throw#
const r = parse<User>(text);
if (!r.success) return badRequest(r.errors);
useUser(r.data);Errors carry the path that failed, so "user.addresses[2].zip" rather than "invalid input".
validate() collects every error, assert() stops at the first#
Use validate for form submissions where the user wants the whole list, assert on internal boundaries where one failure is enough.
Derive OpenAPI components for a whole schema set in one call#
import { toOpenApiComponents } from '@zmdb/schema/openapi';
const { schemas } = toOpenApiComponents([users, posts, comments]);Six variants per schema (entity, create, update, get, list, search), with relations resolved as $refs. See OpenAPI.
Turn a schema into an LLM tool spec#
Install the provider-neutral package with yarn add @zmdb/ai@1.0.0-beta.2; it adds no provider SDK or framework peer.
import { toolFromSchema } from '@zmdb/ai';
const tool = toolFromSchema('create_user', users, { description: 'Create a user' });See LLM Function Calling.
lenientParse survives fenced JSON from a model#
import { lenientParse } from '@zmdb/ai';
lenientParse<User>('```json\n{"email":"a@b.c"}\n```');Strips a leading or trailing code fence before parsing, which is what a model wraps JSON in when you asked for JSON. Prose before the fence is not stripped. See Structured Output.
The test harness boots the real app#
import { createTestApp } from '@zmdb/web/testing';
await using app = createTestApp(RootModule, { overrides: [{ token: DbToken, useValue: fakeDriver }] });
const res = await app.request({ method: 'GET', path: '/users/1' });Same router, same middleware chain, same DI graph — with await using for teardown. See Testing.
Count metadata reads to prove there are none#
The repository-private countMetadataReads probe wraps a controller's Symbol.metadata property, boots the real router, then proves repeated requests add zero reads. This is how the "no runtime reflection" claim is tested rather than asserted; it is test support, not a published application entry point. See Web Performance & Benchmarks.
whereExists takes any compilable#
import { trustedTable } from '@zmdb/sql';
qc.selectFrom(trustedTable('authors')).whereExists(qc.selectFrom(trustedTable('posts')).where('author_id', '=', 1));Anything with a compile() works, including a hand-built CompiledQuery. See Parents with at least one child.
Partial indexes#
IndexDef takes a where clause:
createIndexDdl({ name: 'active_email', table: 'users', columns: ['email'], unique: true, where: 'deleted_at IS NULL' }, 'postgres');---
See also: FAQ · Gotchas · Query Utilities