Skip to content

Schema Kit

Getting started

import * as z from 'zod';
import { getSchemaFromZod, zKysely, renderSchemaAsPDF, generateSQLFromSchema } from '@sluice/schema-kit';

Define a schema using Zod

Zod is a TypeScript-first schema declaration and validation library.

Zod is designed to be as developer-friendly as possible. The goal is to eliminate duplicative type declarations. With Zod, you declare a validator once and Zod will automatically infer the static TypeScript type, and with SluiceDB’s Schema Kit, it will also generate a SQL schema for you.

Define each table as a zKysely schema

const SeasonSchema = zKysely(
z.object({
season: zKysely(z.union([z.literal(0), z.literal(-1), z.literal(-2)]), { primaryKey: true }) .describe(
'An integer representing the season of the event, whether current (0), last year (-1) or the year before (-2)',
)
id: z.number().describe('The robotevents unique identifier for this season'),
name: z.string().describe('The name for this VEX season, such as Over Under'),
start: z.string().pipe(z.coerce.date()).describe('The start date of this season'),
end: z.string().pipe(z.coerce.date()).describe('The end date of this season'),
years_start: z.number().describe('The starting year of this season'),
years_end: z.number().describe('The ending year of this season'),
created_date: z.string().pipe(z.coerce.date()).describe('The timestamp when the record was created'),
last_updated: z.string().pipe(z.coerce.date()).nullable().optional().describe('The timestamp when the record was last updated'),
}),
{},
).describe('Table storing three rows of season information the current year, previous year, and year before that');

Define overall database from table schemas

const DatabaseSchema = zKysely(
z
.object({
season: SeasonSchema,
})
.describe("Schema for 'Test' Database"),
{
namespace: 'dev.sluice.schemas:test',
appName: 'OffGrid Health',
},
);
//

Inferred Database schema for Kysely and TypeScript

type TestDatabase = z.output<typeof DatabaseSchema>;
const db = new Kysely<TestDatabase>({ dialect: ... });

Decompose/reuse/merge subschemas

/** Reusable chunk for season sequence number */
const seasonSeqSchemaPrimary = z
.union([z.literal(0), z.literal(-1), z.literal(-2)])
.describe(
'An integer representing the season of the event, whether current (0), last year (-1) or the year before (-2)',
);
/** Add `created_date` and `last_updated` to Schema */
function schemaWithCreatedUpdated<T extends z.ZodRawShape>(schema: z.ZodObject<T>) {
return schema.merge(
z.object({
created_date: z.string().pipe(z.coerce.date()).describe(descriptions.created_date),
last_updated: z.string().pipe(z.coerce.date()).nullable().optional().describe(descriptions.last_updated),
}),
);
}
const SeasonSchema = zKysely(
schemaWithCreatedUpdated(
z.object({
season: zKysely(seasonSeqSchemaPrimary, { primaryKey: true }),
id: z.number().describe('The robotevents unique identifier for this season'),
name: z.string().describe('The name for this VEX season, such as Over Under'),
start: z.string().pipe(z.coerce.date()).describe('The start date of this season'),
end: z.string().pipe(z.coerce.date()).describe('The end date of this season'),
years_start: z.number().describe('The starting year of this season'),
years_end: z.number().describe('The ending year of this season'),
}),
),
{},
).describe('Table storing three rows of season information the current year, previous year, and year before that');

Convert Zod schema to internal format for SluiceDB / Kysely Query Builder

const schema = getSchemaFromZod(DatabaseSchema);

Render the schema as a pdf

Get an Entity Relationship Diagram and inventory of tables and column definitions in a high quality formatted PDF file.

const DatabaseSchema = zKysely(
z.object({ season: SeasonSchema })
.describe("Schema for 'Robot Events' Database"),
{
namespace: 'com.ogscout.schemas:robotevents',
appName: 'OffGrid Health',
},
);
const schema = getSchemaFromZod(DatabaseSchema, true);
await renderSchemaAsPDF(schema, path.join(CWD, `out/schema-erd.pdf`));

Generate SQL from Schema

With a singular source of truth for your schema, generate SQL for your database and generate TypeScript logic for seamless migrations.

Generate SQL for your database

Terminal window
schema-kit generate-sql --schema ./src/schema.ts --output ./out/data.sql

Generate Migration Logic for your database

Terminal window
schema-kit generate --schema ./src/schema.ts --output ./out/migration.ts