fix(api): load Drizzle migrations at runtime (#2307)

This commit is contained in:
RainbowBird
2026-08-17 11:19:53 +00:00
committed by GitHub
parent 07b26b530f
commit bd790098d2
19 changed files with 11 additions and 236 deletions
-1
View File
@@ -9,7 +9,6 @@ while production deployment configuration remains in `proj-airi/airi-railway`.
- `apps/api`: resource API, business domains, database migrations, and API runtime.
- `apps/auth`: standalone Better Auth and OIDC service.
- `packages/auth-shared`: Auth-owned database schema and principal contracts.
- `packages/drizzle-migration`: bundled migration history consumed by the API migration owner.
- `dev/caddy`: local-only public edge routing for the shared Auth/API origin.
- `docker-compose.yaml`: complete local API + Auth + PostgreSQL + Redis + Caddy stack.
+1 -1
View File
@@ -40,7 +40,7 @@ Local observability is maintained in `proj-airi/airi-railway`; run its `otel/doc
**Layering**:
- **Routes** (`src/routes/`): thin — param validation (Valibot), auth guards, error mapping. No business logic here.
- **Services** (`src/services/`): core business logic and DB transactions.
- **Schemas** (`src/schemas/`): Drizzle table definitions. Migrations in `@proj-airi/drizzle-migration`.
- **Schemas** (`src/schemas/`): Drizzle table definitions. Drizzle loads migrations from `drizzle/` at startup.
**Middleware chain** (`/api/*`): CORS → hono/logger → optional otel → sessionMiddleware → bodyLimit(1MB) → per-route guards. WebSocket `/ws/chat` registered before bodyLimit.
-11
View File
@@ -10,22 +10,11 @@ COPY pnpm-lock.yaml pnpm-workspace.yaml package.json tsconfig.json ./
COPY patches/ ./patches/
COPY server/apps/api server/apps/api
COPY server/packages/auth-shared server/packages/auth-shared
COPY server/packages/drizzle-migration server/packages/drizzle-migration
COPY packages/server-sdk-shared packages/server-sdk-shared
RUN --mount=type=cache,id=pnpm-store,target=/root/.pnpm-store \
pnpm install --frozen-lockfile --ignore-scripts
# NOTICE:
# The Rolldown adapter of unplugin-drizzle-orm-migrations intermittently left
# the virtual import in the output on Railway. The Rollup adapter is compatible
# with tsdown and inlines the migration data. Import the output to verify the
# runtime contract. Do not check a generated chunk name because adapters use
# different output layouts.
RUN rm -rf server/packages/drizzle-migration/dist \
&& pnpm -F @proj-airi/drizzle-migration run build \
&& node --input-type=module -e "import('./server/packages/drizzle-migration/dist/index.mjs').then(({ migrations }) => { if (!Array.isArray(migrations) || migrations.length === 0) throw new Error('drizzle-migration did not export migrations') })"
RUN pnpm -F @proj-airi/server-sdk-shared run build
RUN pnpm -F @proj-airi/api-server run build
+1 -1
View File
@@ -8,7 +8,7 @@ auth/OIDC routes.
- Hono business APIs and WebSocket endpoints.
- Characters, chats, providers, Flux, Stripe, model routing, and billing.
- PostgreSQL migration ownership for the currently shared database.
- PostgreSQL migration ownership for the currently shared database. Drizzle reads the checked-in `drizzle/` journal and SQL files at startup.
- Redis cache, configuration KV, and cross-instance Pub/Sub.
- Local verification of Auth-issued OIDC JWTs through public JWKS.
-2
View File
@@ -44,8 +44,6 @@
"@opentelemetry/sdk-trace-node": "catalog:",
"@opentelemetry/semantic-conventions": "catalog:",
"@proj-airi/auth-shared": "workspace:*",
"@proj-airi/drizzle-migration": "workspace:*",
"@proj-airi/drizzle-orm-browser-migrator": "catalog:",
"@proj-airi/server-sdk-shared": "workspace:*",
"drizzle-orm": "catalog:",
"drizzle-valibot": "catalog:",
@@ -10,21 +10,10 @@ COPY pnpm-lock.yaml pnpm-workspace.yaml package.json tsconfig.json ./
COPY patches/ ./patches/
COPY server/apps/api server/apps/api
COPY server/packages/auth-shared server/packages/auth-shared
COPY server/packages/drizzle-migration server/packages/drizzle-migration
COPY packages/server-sdk-shared packages/server-sdk-shared
RUN pnpm install --frozen-lockfile --ignore-scripts
# NOTICE:
# The Rolldown adapter of unplugin-drizzle-orm-migrations intermittently left
# the virtual import in the output on Railway. The Rollup adapter is compatible
# with tsdown and inlines the migration data. Import the output to verify the
# runtime contract. Do not check a generated chunk name because adapters use
# different output layouts.
RUN rm -rf server/packages/drizzle-migration/dist \
&& pnpm -F @proj-airi/drizzle-migration run build \
&& node --input-type=module -e "import('./server/packages/drizzle-migration/dist/index.mjs').then(({ migrations }) => { if (!Array.isArray(migrations) || migrations.length === 0) throw new Error('drizzle-migration did not export migrations') })"
RUN pnpm -F @proj-airi/server-sdk-shared run build
RUN pnpm -F @proj-airi/api-server run build
-1
View File
@@ -4,7 +4,6 @@ dockerfilePath = "/server/apps/api/production/railway/Dockerfile"
watchPatterns = [
"server/apps/api/**",
"server/packages/auth-shared/**",
"server/packages/drizzle-migration/**",
"packages/server-sdk-shared/**",
"package.json",
"pnpm-lock.yaml",
+6 -4
View File
@@ -1,15 +1,17 @@
import type { Env } from './env'
import { fileURLToPath } from 'node:url'
import pg from 'pg'
import { useLogger } from '@guiiai/logg'
import { migrations } from '@proj-airi/drizzle-migration'
import { migrate } from '@proj-airi/drizzle-orm-browser-migrator/pg'
import { drizzle } from 'drizzle-orm/node-postgres'
import { migrate } from 'drizzle-orm/node-postgres/migrator'
import * as fullSchema from '../schemas'
const logger = useLogger('db')
const migrationsFolder = fileURLToPath(new URL('../../drizzle', import.meta.url))
export type Database = ReturnType<typeof createDrizzle>['db']
@@ -36,6 +38,6 @@ export function createDrizzle(env: DrizzleEnv) {
return { db, pool }
}
export function migrateDatabase(db: Database) {
return migrate(db, migrations)
export async function migrateDatabase(db: Database) {
await migrate(db, { migrationsFolder })
}
+1 -1
View File
@@ -63,4 +63,4 @@ service contract.
- Importing modules from `server/apps/api`.
- Running the shared database migration history during normal process startup.
Auth tables and principal contracts live in `@proj-airi/auth-shared`. The existing `@proj-airi/drizzle-migration` build remains the migration owner while both applications share one PostgreSQL database.
Auth tables and principal contracts live in `@proj-airi/auth-shared`. Drizzle reads shared migration files during API startup. The API remains the migration owner.
+2 -2
View File
@@ -168,8 +168,8 @@ export async function createAuthServer() {
try {
await candidate.db.execute('SELECT 1')
logger.log(`Connected to database on attempt ${attempt}`)
// The drizzle-migration build owns the shared database history.
// Auth startup only checks connectivity and never races migrations.
// The API loads the shared migration history during its startup.
// Auth only checks connectivity and never races migrations.
return candidate
}
catch (error) {
@@ -1,13 +0,0 @@
# `@proj-airi/drizzle-migration`
Build-time package that compiles the SQL migrations from `server/apps/api/drizzle` into an importable module for the API runtime.
## Usage
The API imports the generated `migrations` value and applies it through the Drizzle browser migrator. Build the package from the repository root:
```sh
pnpm -F @proj-airi/drizzle-migration build
```
Use this package for API-owned Drizzle migration bundling. Runtime schemas remain in `server/apps/api/src/schemas`; shared browser or SDK contracts do not belong here.
@@ -1,31 +0,0 @@
{
"name": "@proj-airi/drizzle-migration",
"type": "module",
"version": "0.11.3",
"private": true,
"description": "Drizzle migration for AIRI",
"author": {
"name": "Moeru AI Project AIRI Team",
"email": "airi@moeru.ai",
"url": "https://github.com/moeru-ai"
},
"exports": {
".": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
}
},
"main": "./dist/index.mjs",
"types": "./dist/index.d.mts",
"files": [
"README.md",
"dist",
"package.json"
],
"scripts": {
"build": "tsdown"
},
"devDependencies": {
"@proj-airi/unplugin-drizzle-orm-migrations": "catalog:"
}
}
@@ -1 +0,0 @@
export { default as migrations } from 'virtual:drizzle-migrations.sql'
@@ -1,21 +0,0 @@
{
"compilerOptions": {
"target": "ESNext",
"lib": [
"ESNext"
],
"module": "ESNext",
"moduleResolution": "bundler",
"types": [
"@proj-airi/unplugin-drizzle-orm-migrations/types"
],
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"verbatimModuleSyntax": true,
"skipLibCheck": true
},
"include": [
"src/**/*.ts"
]
}
@@ -1,18 +0,0 @@
import DrizzleORMMigrations from '@proj-airi/unplugin-drizzle-orm-migrations/rollup'
import { defineConfig } from 'tsdown'
export default defineConfig({
entry: [
'src/index.ts',
],
dts: true,
sourcemap: true,
unused: true,
fixedExtension: true,
plugins: [
DrizzleORMMigrations({
root: '../../apps/api',
}),
],
})
@@ -1,7 +0,0 @@
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
include: ['src/**/*.test.ts'],
},
})