Source of truth: PostgreSQL
Kanel is a code generation framework that transforms your PostgreSQL database schema into various output formats. Most commonly, it generates TypeScript types that look like this:
ts
// @generated
// This file is automatically generated by Kanel. Do not modify manually.
import { countryId, type CountryId } from './Country';
import { z } from 'zod';
/** Identifier type for city */
export type CityId = number & { __flavor?: 'CityId' };
/** Represents the table public.city */
export default interface City {
/** Database type: pg_catalog.int4 */
city_id: CityId;
/** Database type: pg_catalog.varchar */
city: string;
/** Database type: pg_catalog.int2 */
country_id: CountryId;
/** Database type: pg_catalog.timestamp */
last_update: Date;
}
/** Represents the initializer for the table public.city */
export interface CityInitializer {
/**
* Database type: pg_catalog.int4
* Default value: nextval('city_city_id_seq'::regclass)
*/
city_id?: CityId;
/** Database type: pg_catalog.varchar */
city: string;
/** Database type: pg_catalog.int2 */
country_id: CountryId;
/**
* Database type: pg_catalog.timestamp
* Default value: now()
*/
last_update?: Date;
}
/** Represents the mutator for the table public.city */
export interface CityMutator {
/** Database type: pg_catalog.int4 */
city_id?: CityId;
/** Database type: pg_catalog.varchar */
city?: string;
/** Database type: pg_catalog.int2 */
country_id?: CountryId;
/** Database type: pg_catalog.timestamp */
last_update?: Date;
}
export const cityId = z.number() as unknown as z.Schema<CityId>;
export const city = z.object({
city_id: cityId,
city: z.string(),
country_id: countryId,
last_update: z.date(),
}) as unknown as z.Schema<City>;
export const cityInitializer = z.object({
city_id: cityId.optional(),
city: z.string(),
country_id: countryId,
last_update: z.date().optional(),
}) as unknown as z.Schema<CityInitializer>;
export const cityMutator = z.object({
city_id: cityId.optional(),
city: z.string().optional(),
country_id: countryId.optional(),
last_update: z.date().optional(),
}) as unknown as z.Schema<CityMutator>;How It Works
Kanel inspects a live PostgreSQL database and uses generators to produce code or documentation. Think of it as a reverse ORM that keeps your database as the source of truth.
The Generator Architecture
Kanel v4 is built around a pluggable generator system:
- PgTsGenerator - Generates TypeScript types (the most common use case)
- MarkdownGenerator - Generates human/LLM-friendly documentation
- Custom generators - You can write generators for Python, Rust, GraphQL, or anything else
javascript
const { makePgTsGenerator, makeMarkdownGenerator } = require('kanel');
module.exports = {
connection: /* ... */,
generators: [
makePgTsGenerator(), // Generate TypeScript types
makeMarkdownGenerator(), // Generate documentation
],
};Why Kanel?
- Database as source of truth - Your schema drives your types, not the other way around
- Type safety - Generate accurate TypeScript types including branded IDs, nullability, and relations
- Extensible - Integrate with Kysely, Zod, Knex, and more through hooks
- Flexible - Write custom generators for any language or format
- Developer workflow - Check generated code into git and treat it as part of your codebase
Quick Start
bash
# Try it instantly
npx kanel -d postgresql://localhost:5432/mydb -o ./src/models
# Or install it
npm i -D kanelSee Getting Started for a complete guide.
Common Use Cases
TypeScript + Kysely
Generate TypeScript types with Kysely database interface:
javascript
const { makePgTsGenerator } = require('kanel');
const { makeKyselyHook } = require('kanel-kysely');
module.exports = {
connection: /* ... */,
generators: [
makePgTsGenerator({
preRenderHooks: [makeKyselyHook()],
}),
],
};TypeScript + Zod Schemas
Generate both TypeScript types and Zod validation schemas:
javascript
const { makePgTsGenerator } = require('kanel');
const { generateZodSchemas } = require('kanel-zod');
module.exports = {
connection: /* ... */,
generators: [
makePgTsGenerator({
preRenderHooks: [generateZodSchemas],
}),
],
};Documentation Only
Generate markdown documentation without TypeScript:
javascript
const { makeMarkdownGenerator } = require('kanel');
module.exports = {
connection: /* ... */,
generators: [makeMarkdownGenerator()],
};See the examples directory for complete configurations.
Learn More
The idea was introduced in this blog post.
Copyright © 2018 Kristian Dupont, licensed under the MIT License