Migrations
Modules can create their own database migrations to add new tables or modify existing ones.
Handle with care
Migrations give you direct access to modify the database structure, this is very powerful but also very dangerous.
So be very careful when writing migration files, always test migrations in a development environment before running them in production.
Running Migrations
Migrations can be run from the admin dashboard or via the CLI.
Use the -m flag to scope the command to your module — this avoids accidentally running or rolling back migrations from other modules:
# Check migrations statuses
node arte migration:status -m mymodule
# Run all pending migrations for your module
node arte migration:latest -m mymodule
# Run one step at a time
node arte migration:up -m mymodule
# Rollback the last migration
node arte migration:down -m mymoduleCreating a Migration
Use the CLI to scaffold a migration file scoped to your module:
node arte migration:make -m mymodule create_table_mymodule_itemsThis creates a file under your module's server/migrations/ directory:
modules/mymodule/server/migrations/2026_01_01_00_00_create_table_mymodule_items.tsNaming convention: Always prefix table names with your module alias followed by __ to avoid conflicts with other modules. For example, mymodule__items instead of items.
Migration File Structure
import { Kysely } from 'kysely'
const table = 'mymodule__items'
export async function up(db: Kysely<any>): Promise<void> {
await db.schema.createTable(table)
.addIdColumn()
.addColumn('name', 'text', col => col.notNull())
.addColumn('description', 'text')
.addTimestampColumns()
.addSoftDeleteColumn()
.execute()
}
export async function down(db: Kysely<any>): Promise<void> {
await db.schema.dropTable(table).execute()
}Extending the Database Contract
After creating migrations, declare your tables in a database.contract.ts file inside your module. Use TypeScript module augmentation to merge your tables into the global Database interface — this enables fully type-safe queries across the entire application.
// modules/mymodule/server/contracts/database.contract.ts
import type { Generated } from 'kysely'
import type { TimestampTable, SoftDeleteTable } from '#server/queries/index.ts'
export interface MyModuleItemsTable extends TimestampTable, SoftDeleteTable {
id: Generated<number>
name: string
description: string | null
}
declare module '#server/contracts/database.contract' {
export interface Database {
mymodule__items: MyModuleItemsTable
}
}Once declared, your tables are available anywhere DatabaseService is used, with full type inference:
import db from '#server/facades/database.facade.ts'
const items = await db.selectFrom('mymodule__items')
.selectAll()
.execute()
// items is typed as MyModuleItemsTable[]