Next.js中MongoDB Atlas API的Client未定义及连接异常问题
问题:Next.js SSR中MongoDB连接报错(MongoExpiredSessionError、MongoTopologyClosedError等)
在Next.js服务端渲染(SSR)组件中,我编写了以下代码用于从MongoDB Atlas的views数据库views集合获取文档浏览量,但运行时抛出大量连接相关错误。
原代码
export async function getViews(id: string) { try { await client.connect(); const collection = client.db("views").collection("views"); const data = await collection.findOne({id}); if(!data) { return 0; } return data.views; } finally { await client.close(); } }
报错信息
MongoExpiredSessionError: Cannot use a session that has ended MongoTopologyClosedError: Topology is closed TypeError: Cannot read properties of undefined (reading 'close') at MongoClient.close (D:\XXXXX\node_modules\mongodb\lib\mongo_client.js:260:18) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async addView (webpack-internal:///(rsc)/./src/app/posts.ts:56:9) ⨯ unhandledRejection: TypeError: Cannot read properties of undefined (reading 'close') at MongoClient.close (D:\XXXXX\node_modules\mongodb\lib\mongo_client.js:260:18) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async addView (webpack-internal:///(rsc)/./src/app/posts.ts:56:9) ⨯ unhandledRejection: TypeError: Cannot read properties of undefined (reading 'close') at MongoClient.close (D:\XXXXX\node_modules\mongodb\lib\mongo_client.js:260:18) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async addView (webpack-internal:///(rsc)/./src/app/posts.ts:56:9) ⨯ TypeError: Cannot read properties of undefined (reading 'close') at async getViews (./src/app/posts.ts:71:9) at async getPostDetails (./src/app/posts.ts:123:24) at async Page (./src/app/blog/[id]/page.tsx:31:15) digest: "486103747" ⨯ MongoTopologyClosedError: Topology is closed at async getViews (./src/app/posts.ts:63:22) at async getPostDetails (./src/app/posts.ts:123:24) at async Page (./src/app/blog/[id]/page.tsx:31:15) digest: "1105313916" ⨯ TypeError: Cannot read properties of undefined (reading 'close') at async getViews (./src/app/posts.ts:71:9) at async getPostDetails (./src/app/posts.ts:123:24) at async Page (./src/app/blog/[id]/page.tsx:31:15) digest: "486103747" ⨯ MongoTopologyClosedError: Topology is closed at async getViews (./src/app/posts.ts:63:22) at async getPostDetails (./src/app/posts.ts:123:24) at async Page (./src/app/blog/[id]/page.tsx:31:15) digest: "1105313916"
问题分析
- 频繁创建/关闭连接:MongoDB客户端设计为长连接复用,每次请求都调用
client.connect()和client.close()会导致连接池混乱,并发请求下会出现会话过期、拓扑关闭等错误。 - 未初始化client的风险:如果
client.connect()失败,client可能未被正确初始化,此时在finally块调用client.close()会触发Cannot read properties of undefined错误。
解决方案
1. 实现MongoDB客户端单例模式
创建lib/mongodb.ts文件,全局复用一个客户端实例:
import { MongoClient } from 'mongodb'; const uri = process.env.MONGODB_URI!; let client: MongoClient; let clientPromise: Promise<MongoClient>; if (!process.env.MONGODB_URI) { throw new Error('Please add your MongoDB URI to .env.local'); } if (process.env.NODE_ENV === 'development') { // 开发环境下避免热重载重复创建客户端 if (!global._mongoClientPromise) { client = new MongoClient(uri); global._mongoClientPromise = client.connect(); } clientPromise = global._mongoClientPromise; } else { // 生产环境直接创建单例 client = new MongoClient(uri); clientPromise = client.connect(); } export default clientPromise;
2. 修改getViews函数
使用单例客户端,不再手动关闭连接:
import clientPromise from '@/lib/mongodb'; export async function getViews(id: string) { const client = await clientPromise; const collection = client.db("views").collection("views"); const data = await collection.findOne({ id }); return data?.views || 0; }
说明
- 单例模式确保全局只有一个MongoDB客户端实例,连接由客户端自身维护的连接池管理,无需手动关闭。
- 避免了并发请求下重复创建/关闭连接导致的错误,同时提升了请求性能。
- 通过可选链操作符
data?.views简化了空值判断,代码更简洁。
内容的提问来源于stack exchange,提问作者Divjot Singh
相关产品推荐
相关产品推荐

