Lucid's schema generator creates TypeScript model schemas by introspecting your database tables. It analyzes table structures and generates type-safe model definitions with proper TypeScript types, decorators, and imports.
import { OrmSchemaGenerator } from '@adonisjs/lucid/orm'
const generator = new OrmSchemaGenerator(
{ outputPath: 'app/models/schemas.ts' },
db,
app
)
await generator.generate()This generates model schemas for all tables in your database and writes them to the specified output file.
For a users table with columns id, email, created_at:
import { BaseModel, column } from '@adonisjs/lucid/orm'
import { DateTime } from 'luxon'
export class UserSchema extends BaseModel {
static $attributes = ['id', 'email', 'createdAt'] as const
@column()
declare id: number
@column()
declare email: string
@column.dateTime()
declare createdAt: DateTime
}Database types are automatically mapped to TypeScript types:
integer,bigint→numbervarchar,text→stringboolean→booleantimestamp,datetime→DateTime(from luxon)date→DateTimejson,jsonb→any- Nullable columns get
| nullunion type
Create custom type mappings and decorators using rules files:
// config/schema_rules.ts
export default {
// Global column name rules
columns: {
status: {
tsType: 'UserStatus',
decorator: '@column()',
imports: [{ source: '#types/enums', namedImports: ['UserStatus'] }]
}
},
// Global type rules
types: {
uuid: {
tsType: 'string',
decorator: '@column()',
imports: []
}
},
// Table-specific rules
tables: {
users: {
columns: {
email: {
tsType: 'string',
decorator: '@column({ isPrimary: true })',
imports: []
}
}
}
}
}Use custom rules:
const generator = new OrmSchemaGenerator(
{
outputPath: 'app/models/schemas.ts',
rulesPaths: ['config/schema_rules.ts']
},
db,
app
)Load and merge multiple rules files for better organization:
const generator = new OrmSchemaGenerator(
{
outputPath: 'app/models/schemas.ts',
rulesPaths: [
'config/rules/base.ts',
'config/rules/enums.ts',
'config/rules/custom_types.ts'
]
},
db,
app
)Rules are merged in order using deep merge strategy - later files override earlier ones.
Rules are applied in this order (most specific to least specific):
- Table-specific column rule:
tables.users.columns.email - Table-specific type rule:
tables.users.types.varchar - Global column rule:
columns.email - Global type rule:
types.varchar - Default built-in mapping
Some columns like id have default rules that take precedence over type rules. If you want to customize the type for UUID primary keys, you need to override the columns.id rule directly.
If all your primary keys are UUIDs:
export default {
columns: {
id: {
tsType: 'UUID',
decorator: '@column({ isPrimary: true })',
imports: [{ source: '#types', namedImports: ['UUID'] }]
}
}
}If you have mixed primary key types (some UUIDs, some integers), use a function:
export default {
columns: {
id: (dataType) => ({
tsType: dataType === 'uuid' ? 'UUID' : dataType === 'bigint' ? 'bigint | number' : 'number',
decorator: '@column({ isPrimary: true })',
imports: dataType === 'uuid' ? [{ source: '#types', namedImports: ['UUID'] }] : []
})
}
}Note
Defining types.uuid alone will not affect the id column because columns.id has higher priority in the lookup hierarchy.
type OrmSchemaGeneratorConfig = {
// Enable or disable schema generation (defaults to true)
enabled?: boolean
// Database connection name (defaults to primary connection)
connectionName?: string
// Output file path (required)
outputPath: string
// Optional rules files to customize type mappings
rulesPaths?: string[]
}To disable schema generation entirely, set enabled: false in your database configuration:
// config/database.ts
{
connection: 'postgres',
connections: {
postgres: {
client: 'pg',
// ...
schemaGeneration: {
enabled: false,
},
},
},
}When disabled, the schema:generate command and automatic generation after migrations will be skipped.
By default, the generator uses your primary database connection. For PostgreSQL, it respects the searchPath configuration:
// Uses searchPath from connection config, defaults to ['public']
const generator = new OrmSchemaGenerator(
{
outputPath: 'schemas.ts',
connectionName: 'postgres' // optional, defaults to primary
},
db,
app
)The generator automatically excludes system tables:
- PostgreSQL:
pg_*,information_schema.* - MySQL:
mysql.*,information_schema.*,performance_schema.* - SQLite:
sqlite_* - MSSQL:
sys.*,INFORMATION_SCHEMA.*
The generator automatically manages imports, consolidating them at the top of the file:
import { BaseModel, column } from '@adonisjs/lucid/orm'
import { DateTime } from 'luxon'
import type { UserStatus } from '#types/enums'
import type { Priority } from '#types/priority'When defining custom rules, you can specify imports using the following options:
imports: [
// Named import: import { Foo } from '#types'
{ source: '#types', namedImports: ['Foo'] },
// Type-only import: import type { Bar } from '#types'
{ source: '#types', typeImports: ['Bar'] },
// Default import: import Baz from '#types'
{ source: '#types', defaultImport: 'Baz' },
// Combined: import Qux, { Foo } from '#types'
{ source: '#types', defaultImport: 'Qux', namedImports: ['Foo'] },
]Use typeImports instead of namedImports when importing TypeScript types or interfaces to generate import type syntax for optimal tree-shaking.
export default {
columns: {
id: {
tsType: 'UUID',
decorator: '@column({ isPrimary: true })',
imports: [{ source: '#types', typeImports: ['UUID'] }]
}
}
}This generates:
import type { UUID } from '#types'