Migration Workflow

Nanoka uses a three-stage pipeline to get your model definitions into a running database. This page explains each stage and the options available at each step.

Pipeline Overview

Model Definition → nanoka generate → Drizzle Schema → drizzle-kit generate → SQL Migrations → wrangler d1 migrations apply → D1

nanoka.config.ts

The config file tells nanoka generate which models to process and where to write output. Use defineConfig from @nanokajs/core/config:

import { defineConfig } from '@nanokajs/core/config'
import { User } from './src/models/user'
import { Post } from './src/models/post'

export default defineConfig({
  output: './drizzle/schema.ts',
  models: [User, Post],
  migrate: {
    drizzleConfig: './drizzle.config.ts',
    database: 'my-database',
    packageManager: 'pnpm',
  },
})

Fields:

Field Type Default Description
output string ./drizzle/schema.ts Output file path for generated Drizzle schema
models Model[] required Array of Nanoka model definitions
migrate.drizzleConfig string ./drizzle.config.ts Path to drizzle.config.ts
migrate.database string D1 database name for wrangler
migrate.packageManager string npx Package manager for CLI commands

drizzle.config.ts

import { defineConfig } from 'drizzle-kit'

export default defineConfig({
  dialect: 'sqlite',
  driver: 'd1-http',
  schema: './drizzle/schema.ts',
  out: './drizzle/migrations',
})

Option A — Unified Pipeline

Run the full pipeline in one command:

npx nanoka generate --apply --db my-database
# Or with remote deploy:
npx nanoka generate --apply --db my-database --remote

Option B — Step by Step

# 1. Generate Drizzle schema
npx nanoka generate

# 2. Generate SQL migrations
npx drizzle-kit generate

# 3. Apply to local D1
npx wrangler d1 migrations apply my-database --local

Option C — Schema Generation Only

npx nanoka generate --no-migrate

This generates the Drizzle schema file but skips all migration steps. Useful for CI diffing and codegen-only workflows.

Remote Deploys

Pass --remote to apply migrations to the production D1 database instead of the local instance:

npx nanoka generate --apply --db my-database --remote

The --remote flag is passed directly to wrangler d1 migrations apply. To inspect the current migration state:

npx wrangler d1 migrations list my-database --remote

Adding a New Model

  1. Create a model file (e.g., src/models/post.ts)
  2. Add it to the models array in nanoka.config.ts
  3. Run the pipeline:
npx nanoka generate --apply --db my-database

Modifying a Field

Nanoka regenerates the Drizzle schema from your updated model definition. drizzle-kit generate then diffs the schema against the previous migration and generates the SQL for the change.

When drizzle-kit detects a rename or drop, it prompts interactively for confirmation. Nanoka does not intercept or automate this step — the diff and SQL generation logic lives entirely in drizzle-kit.

Generated Drizzle Schema Example

Given a User model with typical fields, nanoka generate writes:

import { integer, text, sqliteTable } from 'drizzle-orm/sqlite-core'

export const users = sqliteTable('users', {
  id: text('id').primaryKey().notNull(),
  email: text('email').notNull().unique(),
  name: text('name').notNull(),
  passwordHash: text('passwordHash').notNull(),
  createdAt: integer('createdAt', { mode: 'timestamp_ms' }).notNull(),
})

This file is a standard Drizzle schema — you can inspect it, commit it, and use it directly with any Drizzle query.

Note on t.json() and Codegen

  • t.json() generates: text('data', { mode: 'json' }).$type<unknown>()
  • After generation, manually update $type<unknown>() to $type<MyShape>() to get proper TypeScript types on that column.
  • Function defaults (e.g., t.timestamp().default(() => new Date())) are dropped during codegen with a warning. Add $defaultFn(() => new Date()) manually to the generated schema if you need a runtime default on that column.