fix(api): load Drizzle migrations at runtime (#2307)
This commit is contained in:
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user