Migrations and CLI
Arkormˣ provides CLI helpers for generating models, factories, seeders, and migration classes, and for applying migration classes to schema.prisma.
Initialize config
Use this once per project to scaffold arkormx.config.* and bootstrap the expected directory structure.
npx arkorm initGenerate files
Use generators to create consistent project files quickly:
make:model: creates a model class. Add--allto also generate factory, seeder, and migration.make:factory: creates a factory class for model test/seed data generation.make:seeder: creates a seeder class used by theseedcommand.make:migration: creates a timestamped migration class file.
npx arkorm make:model User
npx arkorm make:model User --all
npx arkorm make:factory User
npx arkorm make:seeder Database
npx arkorm make:migration "create users table"Sync model declarations
models:sync updates declare attributes inside your Arkorm models from the best available schema source.
When the active adapter supports model introspection, Arkorm reads the database structure directly.
Otherwise Arkorm falls back to Prisma models from
schema.prisma.For non-Prisma adapter workflows, Arkorm also uses persisted enum metadata from
.arkormx/column-mappings.jsonso enums defined through migration classes remain available to adapter-backed sync.Scalar fields are mapped to TypeScript types.
Prisma enum fields are emitted with
import type { EnumName } from '@prisma/client'declarations.Adapter-introspected enum fields are emitted as string-literal unions.
Jsonfields are emitted asRecord<string, unknown> | unknown[], and Prisma list fields are emitted asArray<...>.Nullable Prisma fields are emitted as
type | null.Existing non-
declareclass members are preserved.Existing
declarefields are preserved when their current type is already compatible with the generated type.
npx arkorm models:sync
npx arkorm models:sync --schema ./prisma/schema.prisma --models ./src/modelsRun migrations
migrate loads migration classes and applies their up operations using the active migration backend.
Prisma/file-backed flows update
schema.prisma, then optionally run Prisma commands.Adapter-backed flows execute schema operations directly against the database when the adapter supports them.
In adapter-backed flows, Arkorm also rebuilds
.arkormx/column-mappings.jsonfrom the applied migration set so mapped columns and enum definitions remain available at runtime.--all: run all migration class files in the migrations directory.<name>: run one migration class/file by name.--skip-generate: skipprisma generate.--skip-migrate: skipprisma migrate dev/deploy.--deploy: useprisma migrate deployinstead ofprisma migrate dev.--create-database: for adapters that support database creation, create the configured database when missing instead of prompting.
npx arkorm migrate --all
npx arkorm migrate CreateUsersMigration
npx arkorm migrate --all --skip-generate --skip-migrate
npx arkorm migrate --all --deploy
npx arkorm migrate --all --create-databaseRollback migrations
migrate:rollback applies down operations for tracked migration classes and updates migration history state.
When you use adapter-backed migrations, rollback also refreshes .arkormx/column-mappings.json so removed mapped columns and enums disappear from persisted metadata.
- Default behavior: rolls back all migration classes applied by the last
migraterun. --step=<n>: rolls back only the latestnapplied migration classes.--dry-run: previews rollback targets without changing schema/history or running Prisma commands.--skip-generate: skipprisma generate.--skip-migrate: skipprisma migrate dev/deploy.--deploy: run with deploy mode when Prisma migrate execution is enabled.
npx arkorm migrate:rollback
npx arkorm migrate:rollback --step=1
npx arkorm migrate:rollback --dry-run
npx arkorm migrate:rollback --skip-generate --skip-migrateInspect migration history
Use migration history commands to audit or reset tracked migration class state.
For adapter-backed projects, resetting or deleting migration history also clears persisted metadata derived from that state.
migrate:history: prints tracked migration state.--json: prints raw JSON output.--reset: clears tracked entries but keeps the state file.--delete: removes the state file.
npx arkorm migrate:history
npx arkorm migrate:history --json
npx arkorm migrate:history --reset
npx arkorm migrate:history --deleteForeign keys and relation aliases
Use foreignKey in table migrations to generate Prisma relation fields automatically:
schema.createTable('tokens', (table) => {
table.id();
table.integer('userId');
table.string('value');
table
.foreignKey('userId')
.references('users', 'id')
.onDelete('cascade')
.alias('TokenUser');
});This generates a relation field like:
user User @relation("TokenUser", fields: [userId], references: [id], onDelete: Cascade)You can also rename the generated relation field with .as(fieldName):
table
.foreignKey('userId')
.references('users', 'id')
.onDelete('cascade')
.alias('TokenOwner')
.as('owner');Generated relation field:
owner User @relation("TokenOwner", fields: [userId], references: [id], onDelete: Cascade)Arkormˣ also adds the inverse list relation on the referenced model automatically. For a personal_access_tokens -> users foreign key, it generates:
personalAccessTokens PersonalAccessToken[] @relation("TokenUser")If the foreign key column is the latest column you added, you can also chain foreign() directly from that column definition:
schema.createTable('next_of_kins', (table) => {
table.id();
table.uuid('userId').foreign().references('users', 'id').onDelete('cascade');
});That generates a named relation on both sides by default:
model NextOfKin {
user User @relation("NextOfKinUser", fields: [userId], references: [id], onDelete: Cascade)
}
model User {
nextOfKins NextOfKin[] @relation("NextOfKinUser")
}For one-to-one relations, make the foreign key column @unique. If the relation should be optional on the owning side, make the foreign key nullable and alias the relation field explicitly when the column name is not descriptive enough:
schema.alterTable('users', (table) => {
table.uuid('nokId').nullable().unique().map('nok_id');
table.foreignKey('nokId').references('next_of_kins', 'id').as('nextOfKin');
});This generates a one-to-one relation shape:
model User {
nokId String? @unique @map("nok_id")
nextOfKin NextOfKin? @relation("NextOfKinUser", fields: [nokId], references: [id])
}
model NextOfKin {
user User? @relation("NextOfKinUser")
}If you want the owning-side relation to be required, keep the foreign key unique but drop .nullable().
You can override the inverse relation name with .inverseAlias(name) when needed:
table
.foreignKey('userId')
.references('users', 'id')
.alias('TokenOwner')
.inverseAlias('UserTokens')
.as('owner');Enum columns
Use table.enum(...) when a field should map to a Prisma enum type.
schema.createTable('orders', (table) => {
table.id();
table
.enum('status', ['PENDING', 'PAID', 'CANCELLED'])
.enumName('OrderStatus')
.default('PENDING');
});This generates both the model field and the Prisma enum block:
enum OrderStatus {
PENDING
PAID
CANCELLED
}
model Order {
id Int @id @default(autoincrement())
status OrderStatus @default(PENDING)
}Notes:
enumNameis required so Arkorm can emit a stable Prisma enum type name.- Enum defaults should be provided as enum member names like
'PENDING', not quoted Prisma expressions. - Enum defaults must match one of the enum members. Arkorm validates this before writing
schema.prisma. - Enum member names must be valid Prisma identifiers such as
PENDING_REVIEW; spaces and other invalid characters are rejected. - Enum member lists cannot contain duplicates.
- Reusing the same
enumNameacross migrations is supported as long as the enum values match exactly.
You can also reuse an enum that already exists in schema.prisma by passing the enum name instead of redefining its values:
schema.alterTable('invoices', (table) => {
table.enum('status', 'OrderStatus').default('PAID');
});When you reuse an enum by name, Arkorm expects the enum block to already exist in the Prisma schema. If it cannot find that enum, the migration fails. Configured defaults are validated against the reused enum's existing members.
Production runtime notes
When migrations/seeders are authored in TypeScript, production runtime should execute compiled JavaScript, to ensure that everything works as expected, consider the following:
- Keep source structure mirrored in build output.
- Configure
paths.buildOutputto your build root. - Arkormˣ will try to resolve your
.tsfiles with their equivalent.js/.cjs/.mjsin the build output.
If you use a bundler like like tsdown, you can set the unbundle config to true to ensure that your build output mirrors your source structure, if you use other bundlers, check their documentation for similar options.
