mongoose
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMongoose Skill (2025-2026 Edition)
Mongoose Skill (2025-2026 Edition)
This skill provides modern guidelines for using Mongoose with MongoDB, focusing on Mongoose 8.x/9.x, strict TypeScript integration, and performance optimizations relevant to the 2025 ecosystem.
本技能提供了结合MongoDB使用Mongoose的现代指南,重点关注Mongoose 8.x/9.x、严格的TypeScript集成以及适配2025技术生态的性能优化方案。
🚀 Key Trends & Features (2025/2026)
🚀 核心趋势与特性(2025/2026)
- TypeScript-First: Mongoose 8+ has superior built-in type inference. is obsolete.
@types/mongoose - Performance: Mongoose 9 introduces architectural changes for lower overhead. Native vector search support is now standard for AI features.
- Modern JavaScript: Full support for iterators and native Promises.
async/await
- TypeScript优先: Mongoose 8+内置了更优秀的类型推导能力,已被废弃。
@types/mongoose - 性能: Mongoose 9引入了架构层面的改动以降低开销,原生向量搜索支持现已成为AI功能的标准配置。
- 现代JavaScript: 完全支持迭代器和原生Promise。
async/await
📐 TypeScript Integration (The Strict Way)
📐 TypeScript集成(严格模式)
Do NOT extend or standard . Use a plain interface and let Mongoose infer the rest.
LengthyDocumentDocument不要 继承或标准,使用普通接口,让Mongoose自动推导其余类型。
LengthyDocumentDocument1. Define the Interface (Raw Data)
1. 定义接口(原始数据)
Define what your data looks like in plain JavaScript objects.
typescript
import { Types } from 'mongoose';
export interface IUser {
name: string;
email: string;
role: 'admin' | 'user';
tags: string[];
organization?: Types.ObjectId; // Use specific type for ObjectIds
createdAt?: Date;
}定义普通JavaScript对象中的数据结构。
typescript
import { Types } from 'mongoose';
export interface IUser {
name: string;
email: string;
role: 'admin' | 'user';
tags: string[];
organization?: Types.ObjectId; // Use specific type for ObjectIds
createdAt?: Date;
}2. Define the Schema & Model
2. 定义Schema与Model
Create the model with generic typing.
typescript
import mongoose, { Schema, model } from 'mongoose';
import { IUser } from './interfaces';
const userSchema = new Schema<IUser>({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
role: { type: String, enum: ['admin', 'user'], default: 'user' },
tags: [String],
organization: { type: Schema.Types.ObjectId, ref: 'Organization' }
}, {
timestamps: true /* Automatically manages createdAt/updatedAt */
});
// ⚡ 2025 Best Practice: Avoid "extends Document" on the interface.
// Let 'model<IUser>' handle the HydratedDocument type automatically.
export const User = mongoose.models.User || model<IUser>('User', userSchema);使用泛型类型创建model。
typescript
import mongoose, { Schema, model } from 'mongoose';
import { IUser } from './interfaces';
const userSchema = new Schema<IUser>({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
role: { type: String, enum: ['admin', 'user'], default: 'user' },
tags: [String],
organization: { type: Schema.Types.ObjectId, ref: 'Organization' }
}, {
timestamps: true /* 自动管理createdAt/updatedAt字段 */
});
// ⚡ 2025最佳实践:不要在接口上使用"extends Document"
// 让`model<IUser>`自动处理HydratedDocument类型
export const User = mongoose.models.User || model<IUser>('User', userSchema);3. Usage (Type-Safe)
3. 使用(类型安全)
typescript
// 'user' is typed as HydratedDocument<IUser> automatically
const user = await User.findOne({ email: 'test@example.com' });
if (user) {
console.log(user.name); // Typed string
await user.save(); // Typed method
}typescript
// 'user'会被自动推导为HydratedDocument<IUser>类型
const user = await User.findOne({ email: 'test@example.com' });
if (user) {
console.log(user.name); // 推导为string类型
await user.save(); // 类型安全的方法
}⚡ Performance Optimization
⚡ 性能优化
1. .lean()
for Reads
.lean()1. 读操作使用.lean()
.lean()Always use for read-only operations (e.g., GET requests). It skips Mongoose hydration, speeding up queries by 30-50%.
.lean()typescript
// Returns plain JS object (IUser), NOT a Mongoose document
const users = await User.find({ role: 'admin' }).lean(); 只读操作(例如GET请求)始终使用,它会跳过Mongoose的hydration过程,将查询速度提升30-50%。
.lean()typescript
// 返回普通JS对象(IUser类型),而非Mongoose document
const users = await User.find({ role: 'admin' }).lean(); 2. Indexes
2. 索引
Define compound indexes for common query patterns directly in the schema.
typescript
// Schema definition
userSchema.index({ organization: 1, email: 1 }); // Optimized lookup直接在schema中为常用查询模式定义复合索引。
typescript
// Schema定义
userSchema.index({ organization: 1, email: 1 }); // 优化查询效率3. Projections
3. 投影
Fetch only what you need. Network I/O is often the bottleneck.
typescript
const names = await User.find({}, { name: 1 }).lean();仅获取你需要的字段,网络I/O通常是性能瓶颈。
typescript
const names = await User.find({}, { name: 1 }).lean();🛠️ Connection Management (Serverless/Edge Friendly)
🛠️ 连接管理(适配Serverless/Edge环境)
For modern serverless/edge environments (like Vercel, Next.js, Remix), use a singleton pattern with caching to prevent connection exhaustion.
typescript
import mongoose from 'mongoose';
const MONGODB_URI = process.env.MONGODB_URI!;
if (!MONGODB_URI) {
throw new Error('Please define the MONGODB_URI environment variable');
}
/**
* Global is used here to maintain a cached connection across hot reloads
* in development. This prevents connections growing exponentially
* during API Route usage.
*/
let cached = (global as any).mongoose;
if (!cached) {
cached = (global as any).mongoose = { conn: null, promise: null };
}
async function connectDb() {
if (cached.conn) {
return cached.conn;
}
if (!cached.promise) {
const opts = {
bufferCommands: false,
};
cached.promise = mongoose.connect(MONGODB_URI, opts).then((mongoose) => {
return mongoose;
});
}
try {
cached.conn = await cached.promise;
} catch (e) {
cached.promise = null;
throw e;
}
return cached.conn;
}
export default connectDb;对于现代serverless/edge环境(如Vercel、Next.js、Remix),使用带缓存的单例模式来避免连接耗尽。
typescript
import mongoose from 'mongoose';
const MONGODB_URI = process.env.MONGODB_URI!;
if (!MONGODB_URI) {
throw new Error('请定义MONGODB_URI环境变量');
}
/**
* 此处使用global来维护开发环境热重载过程中的缓存连接
* 避免API路由使用过程中连接数指数级增长
*/
let cached = (global as any).mongoose;
if (!cached) {
cached = (global as any).mongoose = { conn: null, promise: null };
}
async function connectDb() {
if (cached.conn) {
return cached.conn;
}
if (!cached.promise) {
const opts = {
bufferCommands: false,
};
cached.promise = mongoose.connect(MONGODB_URI, opts).then((mongoose) => {
return mongoose;
});
}
try {
cached.conn = await cached.promise;
} catch (e) {
cached.promise = null;
throw e;
}
return cached.conn;
}
export default connectDb;