mongoose

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Mongoose 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.
    @types/mongoose
    is obsolete.
  • Performance: Mongoose 9 introduces architectural changes for lower overhead. Native vector search support is now standard for AI features.
  • Modern JavaScript: Full support for
    async/await
    iterators and native Promises.
  • TypeScript优先: Mongoose 8+内置了更优秀的类型推导能力,
    @types/mongoose
    已被废弃。
  • 性能: Mongoose 9引入了架构层面的改动以降低开销,原生向量搜索支持现已成为AI功能的标准配置。
  • 现代JavaScript: 完全支持
    async/await
    迭代器和原生Promise。

📐 TypeScript Integration (The Strict Way)

📐 TypeScript集成(严格模式)

Do NOT extend
LengthyDocument
or standard
Document
. Use a plain interface and let Mongoose infer the rest.
不要 继承
LengthyDocument
或标准
Document
,使用普通接口,让Mongoose自动推导其余类型。

1. 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

1. 读操作使用
.lean()

Always use
.lean()
for read-only operations (e.g., GET requests). It skips Mongoose hydration, speeding up queries by 30-50%.
typescript
// Returns plain JS object (IUser), NOT a Mongoose document
const users = await User.find({ role: 'admin' }).lean(); 
只读操作(例如GET请求)始终使用
.lean()
,它会跳过Mongoose的hydration过程,将查询速度提升30-50%。
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;

📚 References

📚 参考资料