Skip to content

Latest commit

 

History

History
289 lines (224 loc) · 6.56 KB

File metadata and controls

289 lines (224 loc) · 6.56 KB

Schema Generation in Lucid

Overview

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.

Basic Usage

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.

Generated Output

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
}

Type Mapping

Database types are automatically mapped to TypeScript types:

  • integer, bigintnumber
  • varchar, textstring
  • booleanboolean
  • timestamp, datetimeDateTime (from luxon)
  • dateDateTime
  • json, jsonbany
  • Nullable columns get | null union type

Custom Rules

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
)

Multiple Rules Files

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.

Rule Lookup Hierarchy

Rules are applied in this order (most specific to least specific):

  1. Table-specific column rule: tables.users.columns.email
  2. Table-specific type rule: tables.users.types.varchar
  3. Global column rule: columns.email
  4. Global type rule: types.varchar
  5. Default built-in mapping

Customizing Primary Key Types

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.

Configuration Options

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[]
}

Disabling Schema Generation

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.

Connection Handling

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
)

System Tables

The generator automatically excludes system tables:

  • PostgreSQL: pg_*, information_schema.*
  • MySQL: mysql.*, information_schema.*, performance_schema.*
  • SQLite: sqlite_*
  • MSSQL: sys.*, INFORMATION_SCHEMA.*

Import Management

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'

Import Options

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'