☰
TypeScript 学习笔记(九):TypeScript 与数据库的结合应用——用 TaoToken 统一 Key 打通 TypeORM 与 Mongoose 调试链路
2026/10/8 17:48:22 网站建设 项目流程

1. 双数据库项目里,本地调试为什么总在 Key 上卡住

TypeScript 项目里同时接 MySQL 和 MongoDB 并不稀奇:订单、账务这类强关系数据放 MySQL,日志、配置、内容快照这类半结构化数据放 MongoDB。真正让人头疼的不是写实体和 Schema,而是本地调试阶段两条链路各自为政——TypeORM 连一个地址,Mongoose 连另一个地址,模型调用又要走一层 HTTP 接口,Key 散落在.env、ormconfig.json、mongoose.connect()三个地方。改一次环境,三处都要动,漏一处就报 401 或者连接超时。

这篇笔记聚焦一个具体场景:你在本地用 TypeScript 同时接入 MySQL(TypeORM)与 MongoDB(Mongoose),并且模型推理/调试请求需要统一走一个入口。我会给出可复制的tsconfig路径映射、TypeORMDataSource配置、Mongoose 连接片段,然后把本地调试请求的 endpoint 与 Base URL 改到 TaoToken,用同一把 Key 验证两条数据库链路读写是否正常。适合已经写过 TypeORM 实体、用过 Mongoose Schema,但在多模型联调时被 Key 和地址管理搞烦的人。

核心检索词先摆出来:TypeScript 数据库结合应用、TypeORM 连接 MySQL、Mongoose 连接 MongoDB、TaoToken 统一 Key 调试。这四个词贯穿全文,你按顺序跟做即可。

先说清楚 TaoToken 在这里的角色:它是一个统一的模型调用入口,提供兼容常见 SDK 的 Base URL 和 API Key。你不需要在代码里硬编码多个厂商的地址,把 Base URL 指向https://taotoken.net/api,Key 用同一把,模型 ID 按需切换。数据库本身还是连你本地的 MySQL 和 MongoDB,TaoToken 管的是模型调用这条链路,两者不冲突。这一点必须先分清,否则后面配置会乱。

我试过把两条链路的调试请求都收敛到一个.env里,配合tsconfig的路径别名,改地址只改一行。下面从项目结构开始。

2. TaoToken 前置准备:Base URL、Key 与模型 ID 三件套

在动手改数据库配置之前,先把模型调用这条链路的前置条件备齐。TaoToken 的接入只需要三样东西:Base URL、API Key、Model ID。这三件套在后续 TypeORM 和 Mongoose 的调试脚本里会反复出现,所以先统一放好。

Base URL 固定为https://taotoken.net/api,注意这里不带任何查询参数,直接作为 SDK 的baseURL或base_url使用。API Key 需要你在控制台创建,创建入口在 API Keys 页面,登录后新建一个 Key,复制出来存到本地.env,不要提交到 Git。Model ID 取决于你要调用的模型,比如做代码补全、结构化抽取、文本润色时选对应的模型标识即可,具体可选列表在模型对话页面能看到。

这里给一个.env的写法,把数据库配置和模型配置放在一起,方便统一管理:

# .env # MySQL DB_HOST=127.0.0.1 DB_PORT=3306 DB_USERNAME=root DB_PASSWORD=your_mysql_password DB_DATABASE=ts_demo # MongoDB MONGO_URI=mongodb://127.0.0.1:27017/ts_demo # TaoToken 模型调用 TAOTOKEN_BASE_URL=https://taotoken.net/api TAOTOKEN_API_KEY=sk-你的Key TAOTOKEN_MODEL_ID=你的模型ID

注意.env要加进.gitignore。很多 401 报错不是 Key 错了,而是 Key 被提交后轮换了,本地还是旧值。这个坑后面排障章节会再提。

关于 Key 的获取和模型选择,你可以直接去控制台操作:API Keys 页面创建 Key,模型对话页面确认模型 ID。这两个页面是后续所有配置的来源,建议先打开确认一遍再往下走。

如果你打算长期在这个项目里做编码和 Agent 联调,而不是只跑一次验证,可以考虑 Coding Plan,它更适合持续性的编码场景。但本篇的重点是本地调试链路打通,先用按量 Key 验证即可。

三件套备齐后,进入项目结构配置。这里有个细节:TAOTOKEN_BASE_URL我建议写成不带尾部斜杠的形式,因为部分 SDK 在拼接/v1/chat/completions时对尾部斜杠处理不一致,带斜杠可能出现双斜杠路径,导致 404。这个在后面验证请求时会看到实际影响。

3. 可复制配置:tsconfig 路径映射 + TypeORM DataSource + Mongoose 连接

这一节是全文的核心,给出三份可直接复制的配置。先看项目结构,我按下面这样组织:

ts-db-demo/ ├── src/ │ ├── entity/ │ │ └── User.ts │ ├── models/ │ │ └── Log.ts │ ├──>{ "compilerOptions": { "target": "ES2020", "module": "commonjs", "moduleResolution": "node", "experimentalDecorators": true, "emitDecoratorMetadata": true, "strict": true, "esModuleInterop": true, "skipLibCheck": true, "outDir": "dist", "baseUrl": ".", "paths": { "@entity/*": ["src/entity/*"], "@models/*": ["src/models/*"], "@root/*": ["src/*"] } }, "include": ["src/**/*.ts"] }

experimentalDecorators和emitDecoratorMetadata是 TypeORM 实体装饰器必需的,缺了会报Unable to resolve signature of class decorator。paths里的baseUrl必须是.,否则别名解析不到。如果你用ts-node直接跑,还需要在package.json里加ts-node的require配置,或者用tsconfig-paths注册,否则运行时会找不到别名模块。

3.2 TypeORM DataSource 配置

TypeORM 0.3 之后推荐用DataSource而不是旧的createConnection。src/data-source.ts:

import 'reflect-metadata'; import 'dotenv/config'; import { DataSource } from 'typeorm'; import { User } from '@entity/User'; export const AppDataSource = new DataSource({ type: 'mysql', host: process.env.DB_HOST, port: parseInt(process.env.DB_PORT || '3306', 10), username: process.env.DB_USERNAME, password: process.env.DB_PASSWORD, database: process.env.DB_DATABASE, synchronize: true, logging: false, entities: [User], migrations: [], subscribers: [], });

entities这里直接写类引用,比 glob 字符串更稳,尤其在ts-node下 glob 有时匹配不到。synchronize: true只建议本地开发用,生产必须关掉,否则会改表结构。

3.3 Mongoose 连接配置

src/mongo.ts:

import 'dotenv/config'; import mongoose from 'mongoose'; export async function connectMongo(): Promise<typeof mongoose> { const uri = process.env.MONGO_URI as string; if (!uri) { throw new Error('MONGO_URI is not defined'); } mongoose.set('strictQuery', true); await mongoose.connect(uri, { serverSelectionTimeoutMS: 5000, }); console.log('MongoDB connected'); return mongoose; }

serverSelectionTimeoutMS设成 5000 是为了本地连不上时快速失败,而不是默认 30 秒干等。strictQuery设 true 可以避免查询里带未知字段时静默忽略。

3.4 模型调试脚本

src/debug-model.ts用同一把 Key 调 TaoToken,验证模型链路:

import 'dotenv/config'; const BASE_URL = process.env.TAOTOKEN_BASE_URL as string; const API_KEY = process.env.TAOTOKEN_API_KEY as string; const MODEL_ID = process.env.TAOTOKEN_MODEL_ID as string; export async function pingModel(prompt: string): Promise<string> { const res = await fetch(`${BASE_URL}/v1/chat/completions`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${API_KEY}`, }, body: JSON.stringify({ model: MODEL_ID, messages: [{ role: 'user', content: prompt }], }), }); if (!res.ok) { const text = await res.text(); throw new Error(`Model request failed: ${res.status} ${text}`); } const data = (await res.json()) as { choices: Array<{ message: { content: string } }>; }; return data.choices[0].message.content; }

注意BASE_URL后面拼的是/v1/chat/completions,所以.env里的TAOTOKEN_BASE_URL不要带尾部斜杠,否则会变成//v1/...。这个细节在排障章节会对应到具体报错。

三份配置就位后,package.json的脚本可以这样写:

{ "scripts": { "dev:mysql": "ts-node -r tsconfig-paths/register src/run-mysql.ts", "dev:mongo": "ts-node -r tsconfig-paths/register src/run-mongo.ts", "dev:model": "ts-node -r tsconfig-paths/register src/run-model.ts" } }

tsconfig-paths/register是让运行时也认@entity/*这类别名的关键,少了它ts-node会报Cannot find module '@entity/User'。

4. 验证请求:同一把 Key 跑通两条数据库链路

配置写完,接下来是实际验证。验证分三步:先确认 MySQL 读写,再确认 MongoDB 读写,最后确认模型调用链路,三者用同一套.env。

4.1 验证 TypeORM 读写

src/run-mysql.ts:

import { AppDataSource } from '@root/data-source'; import { User } from '@entity/User'; async function main() { await AppDataSource.initialize(); console.log('MySQL connected'); const repo = AppDataSource.getRepository(User); const saved = await repo.save( repo.create({ firstName: 'John', lastName: 'Doe', age: 25 }) ); console.log('Saved user id:', saved.id); const all = await repo.find(); console.log('All users:', all); await AppDataSource.destroy(); } main().catch((err) => { console.error('MySQL error:', err); process.exit(1); });

运行npm run dev:mysql,预期输出:

MySQL connected Saved user id: 1 All users: [ User { id: 1, firstName: 'John', lastName: 'Doe', age: 25 } ]

如果Saved user id有值且All users能查到,说明 TypeORM 链路正常。

4.2 验证 Mongoose 读写

src/run-mongo.ts:

import { connectMongo } from '@root/mongo'; import Log from '@models/Log'; async function main() { await connectMongo(); const created = await Log.create({ level: 'info', message: 'mongo write ok', createdAt: new Date(), }); console.log('Saved log id:', created._id.toString()); const logs = await Log.find().limit(5); console.log('Recent logs:', logs); } main().catch((err) => { console.error('Mongo error:', err); process.exit(1); });

运行npm run dev:mongo,预期输出里能看到Saved log id和Recent logs数组。两条数据库链路都通了,说明本地 MySQL 和 MongoDB 服务正常,配置也没问题。

4.3 验证模型调用链路

src/run-model.ts:

import { pingModel } from '@root/debug-model'; async function main() { const reply = await pingModel('用一句话说明 TypeScript 的类型收窄'); console.log('Model reply:', reply); } main().catch((err) => { console.error('Model error:', err); process.exit(1); });

运行npm run dev:model。如果返回一段正常文本,说明 Base URL、Key、Model ID 三件套都对。如果报 401,先查 Key;如果报 404,先查 Base URL 尾部斜杠;如果报reading 'choices',说明响应结构不是预期的 chat completions 格式,多半是 Model ID 或路径不对。

4.4 把两条链路串起来

真正体现「统一 Key」价值的是把数据库写入和模型调用放在一个脚本里。比如写入一条日志后,让模型对日志内容做摘要,再写回 MongoDB:

import { connectMongo } from '@root/mongo'; import Log from '@models/Log'; import { pingModel } from '@root/debug-model'; async function main() { await connectMongo(); const raw = '用户登录失败三次,IP 归属地异常'; const summary = await pingModel(`请用不超过20字总结:${raw}`); await Log.create({ level: 'warn', message: raw, summary }); console.log('Summary saved:', summary); } main().catch((err) => { console.error(err); process.exit(1); });

这个脚本同时用到 Mongoose 和 TaoToken,Key 只有一把,地址只有一处。跑通它,说明你的多模型联调链路已经收敛。

5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth

这一节按真实报错来对照,每个报错给出定位方法和修复动作。

401 Unauthorized。最常见的原因是 Key 没读到或已失效。先确认.env里TAOTOKEN_API_KEY没有多余空格和引号,再确认脚本里import 'dotenv/config'在读取process.env之前执行。如果 Key 是从控制台复制的,注意不要漏掉前缀。还有一种情况是 Key 被提交到 Git 后轮换了,本地还是旧值,重新在 API Keys 页面生成一个替换即可。

local proxy failed / connect ECONNREFUSED。这个报错通常出现在你本地配了 HTTP 代理,但代理进程没起来,或者NO_PROXY没排除本地地址。检查系统环境变量里的HTTP_PROXY、HTTPS_PROXY,如果不需要代理就清掉;如果本地 MySQL/MongoDB 连不上,也会报ECONNREFUSED 127.0.0.1:3306或:27017,这时先确认数据库服务是否启动,端口是否被占用。

Cannot read properties of undefined (reading 'choices')。这个报错说明data.choices是 undefined,即响应体不是标准的 chat completions 结构。三种可能:Base URL 少了/v1或多了尾部斜杠导致路径错误;Model ID 写错导致返回了错误对象;请求体里messages格式不对。修复方式是先打印res.status和原始文本,确认返回内容再调整。

OAuth / token 相关报错。如果你用的是某些 CLI 工具(比如 Claude Code 这类),它可能走 OAuth 流程而不是 API Key。这种情况下要确认工具支持 API Key 模式,并把 Base URL 指向https://taotoken.net/api,Key 用控制台生成的。如果工具只认 OAuth,那就换用支持 Key 的调用方式,或者参考接入文档里的说明。

Cannot find module '@entity/User'。这是路径别名在运行时没生效。确认package.json脚本里带了-r tsconfig-paths/register,并且tsconfig.json的baseUrl是.。如果还不行,检查include是否覆盖了src/**/*.ts。

MongooseServerSelectionError。MongoDB 连不上,先确认MONGO_URI里的库名和端口,再确认 MongoDB 服务是否允许本地连接。本地开发一般不需要认证,如果开了认证要补用户名密码。

TypeORM EntityMetadataNotFoundError。实体没被注册。检查data-source.ts的entities数组是否包含对应实体类,用类引用而不是字符串 glob 更稳。

排障时建议按「先数据库、再模型」的顺序,因为数据库报错和模型报错的表现完全不同,混在一起排查容易乱。数据库链路用npm run dev:mysql和npm run dev:mongo单独验证,模型链路用npm run dev:model单独验证,都通过后再跑串联脚本。

6. 把 Key 收敛到一处,后续联调才不返工

走到这里,你的项目应该已经能用同一把 Key 跑通 TypeORM 写 MySQL、Mongoose 写 MongoDB、以及模型调用三条链路。回头看,真正省事的地方在于把 Base URL 和 Key 收敛到.env一处,tsconfig路径映射让导入不再依赖相对层级,DataSource和mongoose.connect各自只读环境变量。

后续如果你要加更多模型调用,比如做结构化抽取、代码补全、日志摘要,只需要在debug-model.ts里复用pingModel,换 Model ID 即可,不用再动数据库配置。如果你要长期在这个项目里做编码和 Agent 联调,可以了解 Coding Plan,它更适合持续性的编码场景;如果只是验证模型效果,模型对话页面能直接试。

最后留一个实用技巧:把TAOTOKEN_BASE_URL、TAOTOKEN_API_KEY、TAOTOKEN_MODEL_ID三个变量在.env.example里留空模板,提交到仓库,真实.env不提交。这样团队里每个人拉下来只需要填自己的 Key,数据库地址和模型地址保持一致,联调时不会再出现「你那边能跑我这边 401」的情况。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询