feat(api): NestJS standalone 骨架 + Drizzle/postgres.js 接入

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-10-20 18:56:50 +08:00
parent 6725bf0024
commit 514f37301e
9 changed files with 1367 additions and 10 deletions
+9
View File
@@ -0,0 +1,9 @@
import './src/db/env.js'
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
dialect: 'postgresql',
schema: './src/db/schema.ts',
out: './drizzle',
dbCredentials: { url: process.env.DATABASE_URL! },
})
+25
View File
@@ -0,0 +1,25 @@
{
"name": "@sf/api",
"version": "0.1.0",
"description": "SiteForge API:NestJS 模块化单体骨架(本切片以 standalone context 供 CLI 使用,不监听端口)+ Drizzle/postgres.js 接入",
"type": "module",
"scripts": {
"build": "tsc -p tsconfig.json",
"db:generate": "drizzle-kit generate",
"db:migrate": "drizzle-kit migrate"
},
"dependencies": {
"@nestjs/common": "^10.4.22",
"@nestjs/core": "^10.4.22",
"drizzle-orm": "^0.45.4",
"postgres": "^3.4.9",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1"
},
"devDependencies": {
"@types/node": "^26.6.5",
"dotenv": "^18.0.8",
"drizzle-kit": "^0.31.11",
"typescript": "^5.6.0"
}
}
+4
View File
@@ -0,0 +1,4 @@
import { Module } from '@nestjs/common'
@Module({ imports: [] })
export class AppModule {}
+13
View File
@@ -0,0 +1,13 @@
import './env.js'
import { drizzle } from 'drizzle-orm/postgres-js'
import postgres from 'postgres'
import * as schema from './schema.js'
// DATABASE_URL_TEST ?? DATABASE_URL 的回退链是 Task 13 集成测试的接线:
// 测试临时库优先,开发库兜底。env 加载见 ./env.ts(向上查找仓库根 .env)。
export function getDb(
connectionString = process.env.DATABASE_URL_TEST ?? process.env.DATABASE_URL!,
) {
const sql = postgres(connectionString, { max: 5 })
return drizzle(sql, { schema })
}
+18
View File
@@ -0,0 +1,18 @@
import { existsSync } from 'node:fs'
import { dirname, join } from 'node:path'
import { config } from 'dotenv'
// dotenv 默认只读 cwd 下的 .env:从仓库根跑 `pnpm test` 时 cwd 是仓库根,能读到根 .env;
// 但在 apps/api 内跑 drizzle-kit(db:generate / db:migrate)时 cwd 是包目录,默认读不到。
// 因此这里从 cwd 逐级向上查找 .env(就近优先,找到即停),两种场景都能稳定读到仓库根 .env。
let dir = process.cwd()
for (;;) {
const candidate = join(dir, '.env')
if (existsSync(candidate)) {
config({ path: candidate })
break
}
const parent = dirname(dir)
if (parent === dir) break
dir = parent
}
+2
View File
@@ -0,0 +1,2 @@
// 聚合导出:后续任务(6-12)按域追加 pgTable 定义
export {}
+12
View File
@@ -0,0 +1,12 @@
import { describe, it, expect } from 'vitest'
import { getDb } from '../src/db/client.js'
describe('数据库连接', () => {
it('连接本项目 PG(5434)并响应 select 1', async () => {
const db = getDb()
// postgres.js 驱动下 drizzle 的 execute 返回 RowList(行数组),
// 不是 node-postgres 风格的 { rows: [...] },故直接取 r[0]
const r = (await db.execute('select 1 as ok')) as any
expect(Number(r[0].ok)).toBe(1)
})
})
+9
View File
@@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"experimentalDecorators": true,
"emitDecoratorMetadata": true
},
"include": ["src", "test"]
}
+1275 -10
View File
File diff suppressed because it is too large Load Diff