Convert database schemas to TypeScript types, Zod schemas, or Kysely definitions.
- Supports: MySQL, PostgreSQL, SQLite, Prisma, SQL DDL Files
- Features: Views, Magic Comments, Type Overrides, Multiple Outputs
npm install mutanoimport{generate}from'mutano'// Basic usage with databaseawaitgenerate({origin: {type: 'mysql',// or 'postgres', 'sqlite', 'prisma', 'sql'host: '127.0.0.1',port: 3306,user: 'root',password: 'secret',database: 'myapp'},destinations: [{type: 'zod',// or 'ts', 'kysely'folder: './generated'}]})// Using SQL DDL files (no database required)awaitgenerate({origin: {type: 'sql',path: './schemas/myapp.sql',// Path to SQL filedialect: 'mysql'// or 'postgres', 'sqlite'},destinations: [{type: 'zod',folder: './generated'}]})// Multiple outputsawaitgenerate({origin: {/* ... */},destinations: [{type: 'zod',folder: './zod'},{type: 'ts',folder: './types'},{type: 'kysely',outFile: './db.ts'}]})// With views supportawaitgenerate({origin: {/* ... */},destinations: [{type: 'zod'}],includeViews: true,views: ['user_profile_view'],// optional filterignoreViews: ['temp_view']// optional exclude})Zod Schema:
exportconstuser=z.object({id: z.number().nonnegative(),name: z.string().min(1),email: z.string().email(),role: z.enum(['admin','user']),})exportconstinsertable_user=z.object({name: z.string().min(1),email: z.string().email(),role: z.enum(['admin','user']),})exporttypeUserType=z.infer<typeofuser>TypeScript Interface:
exportinterfaceUser{id: number;name: string;email: string;role: 'admin'|'user';}exportinterfaceInsertableUser{name: string;email: string;role: 'admin'|'user';}Kysely Types:
exportinterfaceUser{id: Generated<number>;name: string;email: string;role: 'admin'|'user';}exporttypeSelectableUser=Selectable<User>;exporttypeInsertableUser=Insertable<User>;exporttypeUpdateableUser=Updateable<User>;// MySQL/PostgreSQL{type: 'mysql'|'postgres',host: string,port: number,user: string,password: string,database: string,schema?: string,// PostgreSQL onlyssl?: { ca, cert, key }}// SQLite{type: 'sqlite',path: string}// Prisma{type: 'prisma',path: string// Path to a .prisma file or a directory containing .prisma files}// SQL DDL Files (no database required){type: 'sql',path: string,// Path to .sql file with CREATE TABLE statementsdialect: 'mysql'|'postgres'|'sqlite'// Determines type mappings}{type: 'zod'|'ts'|'kysely',folder?: string,suffix?: string,outFile?: string,// Kysely onlyheader?: string,// Custom imports// Zod specificuseDateType?: boolean,useBooleanType?: boolean,useTrim?: boolean,nullish?: boolean,requiredString?: boolean,version?: 3|4,// TypeScript specificenumType?: 'union'|'enum',modelType?: 'interface'|'type',// Kysely specificschemaName?: string// Default: 'DB'}useDateType: Whentrue, generatesz.union([z.number(), z.string(), z.date()]).pipe(z.coerce.date())instead ofz.date()for date fieldsuseBooleanType: Whentrue, generatesz.union([z.number(), z.string(), z.boolean()]).pipe(z.coerce.boolean())instead ofz.boolean()for boolean fieldsuseTrim: Whentrue, adds.trim()to string fieldsnullish: Whentrue, uses.nullish()instead of.nullable()for nullable fields (except selectable schemas)requiredString: Whentrue, adds.min(1)validation to required string fieldsversion: Zod version (3 or 4) for compatibility
| Option | Description |
|---|---|
tables | Include only specified tables |
views | Include only specified views |
ignore | Exclude specified tables (supports regex) |
ignoreViews | Exclude specified views (supports regex) |
includeViews | Process database views |
camelCase | Convert to camelCase |
dryRun | Return content without writing files |
magicComments | Enable @zod/@ts/@kysely comments (Obs.: no SQLite support) |
inflection | Transform model names: 'singular', 'plural', or 'none' (default) |
overrideTypes | Override types globally per destination (see below) |
overrideColumns | Override specific columns per table (see below) |
Transform table/view names to singular or plural form in generated types:
// Singular inflection - "users" table becomes "User" typeawaitgenerate({origin: {/* ... */},destinations: [{type: 'zod'}],inflection: 'singular'})// Plural inflection - "user" table becomes "Users" typeawaitgenerate({origin: {/* ... */},destinations: [{type: 'ts'}],inflection: 'plural'})Examples:
| Table Name | inflection: 'singular' | inflection: 'plural' | inflection: 'none' |
|---|---|---|---|
users | UserType | UsersType | UsersType |
companies | CompanyType | CompaniesType | CompaniesType |
people | PersonType | PeopleType | PeopleType |
categories | CategoryType | CategoriesType | CategoriesType |
user_accounts | UserAccountType | UserAccountsType | UserAccountsType |
Works with all output types (Zod, TypeScript, Kysely) and combines with camelCase option.
Override types for specific columns using database comments:
CREATETABLE `user` (
`id`int(11) NOT NULL AUTO_INCREMENT,
`name`varchar(255) COMMENT '@zod(z.string().min(2).max(50))',
`email`varchar(255) COMMENT '@ts(EmailAddress) @kysely(string)',
`metadata` json COMMENT '@ts(UserMetadata)',
`password_hash`varchar(255) COMMENT '@ignore',
PRIMARY KEY (`id`)
);Supported Comments:
@zod(...)- Override Zod schema@ts(...)- Override TypeScript type@kysely(...)- Override Kysely type@ignore- Exclude column from generated types@@ignore- Exclude table/model from generated types
Use @ignore and @@ignore directives to exclude columns and tables from code generation:
Ignore specific fields:
modelUser {idInt@id@default(autoincrement())emailString@uniquepasswordString@ignore// This field will be excludedcreatedAtDateTime@default(now())}Ignore entire models:
modelAuditLog {idInt@id@default(autoincrement())actionStringuserIdInttimestampDateTime@default(now())@@ignore// This entire model will be excluded}Ignore specific columns in MySQL:
ALTERTABLE users MODIFY COLUMN password_hash VARCHAR(255) COMMENT '@ignore';
ALTERTABLE users MODIFY COLUMN internal_id VARCHAR(100) COMMENT 'Internal tracking @ignore';Ignore specific columns in PostgreSQL:
COMMENT ON COLUMN users.password_hash IS '@ignore';
COMMENT ON COLUMN users.internal_id IS 'Internal tracking @ignore';Ignore entire tables in MySQL:
ALTERTABLE audit_logs COMMENT ='@@ignore';
ALTERTABLE internal_metrics COMMENT ='Internal table @@ignore';Ignore entire tables in PostgreSQL:
COMMENT ON TABLE audit_logs IS '@@ignore';
COMMENT ON TABLE internal_metrics IS 'Internal table @@ignore';Example with mixed ignored and non-ignored columns:
CREATETABLE `user` (
`id`int(11) NOT NULL AUTO_INCREMENT,
`email`varchar(255) NOT NULL,
`name`varchar(255),
`password_hash`varchar(255) COMMENT '@ignore',
`internal_tracking_id`varchar(100) COMMENT 'Internal use only @ignore',
`metadata` json COMMENT '@ts(UserMetadata)',
PRIMARY KEY (`id`)
);Generated types will only include: id, email, name, and metadata
Override default types globally. Define destination-specific overrides for each output type:
{origin: {type: 'mysql',// ... connection config},overrideTypes: {zod: {json: 'z.record(z.string())',text: 'z.string().max(1000)',decimal: 'z.number().positive()'},ts: {json: 'Record<string, string>',text: 'string',decimal: 'number'},kysely: {json: 'Json',text: 'string',decimal: 'Decimal'}}}Single destination with multiple outputs:
{origin: {type: 'postgres',// ...},destinations: [{type: 'zod',folder: './zod'},{type: 'ts',folder: './types'},{type: 'kysely',outFile: './db.ts'}],overrideTypes: {zod: {jsonb: 'z.record(z.string())'},ts: {jsonb: 'Record<string, string>'},kysely: {jsonb: 'Json'}}}Overriding tinyint(1) to a Zod boolean coercion (with preserved defaults):
When using SQL DDL files with MySQL tinyint(1) columns, you can override the generated type while preserving default values:
CREATETABLEsettings (
id intNOT NULL AUTO_INCREMENT,
is_active tinyint(1) NOT NULL DEFAULT '1',
is_public tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (id)
);awaitgenerate({origin: {type: 'sql',path: './schema.sql',dialect: 'mysql'},destinations: [{type: 'zod',folder: './generated',useBooleanType: true}],overrideTypes: {zod: {'tinyint(1)': 'z.union([z.number(), z.string(), z.boolean()]).pipe(z.coerce.boolean())'}}})This generates correct defaults from the SQL DEFAULT clause:
// is_active has DEFAULT '1' -> .default(true)// is_public has DEFAULT '0' -> .default(false)exportconstsettings=z.object({is_active: z.union([z.number(),z.string(),z.boolean()]).pipe(z.coerce.boolean()).default(true),is_public: z.union([z.number(),z.string(),z.boolean()]).pipe(z.coerce.boolean()).default(false),})Note:
overrideTypespreserves nullability, optionality, and default values from the schema. Only the base type is replaced. For boolean columns, mutano correctly maps MySQLDEFAULT '1'totrueandDEFAULT '0'tofalse.
Common Overrides:
- MySQL:
json,text,decimal,enum - PostgreSQL:
jsonb,uuid,text,numeric - SQLite:
json,text,real - Prisma:
Json,String,Decimal
Override specific columns from specific tables. Takes priority over magic comments:
{origin: {type: 'mysql',// ... connection config},destinations: [{type: 'zod',folder: './zod'},{type: 'ts',folder: './types'},{type: 'kysely',outFile: './db.ts'}],overrideColumns: {zod: {users: {email: 'z.string().email()',metadata: 'z.record(z.unknown())'},posts: {content: 'z.string().max(10000)'}},ts: {users: {email: 'EmailAddress',metadata: 'Record<string, unknown>'}},kysely: {users: {metadata: 'CustomJsonType'}}}}Priority order (highest to lowest):
overrideColumns- Specific column overrides- Magic comments (
@zod,@ts,@kysely) - Column-level comments overrideTypes- Global type overrides- Default type mappings