1. 安装依赖
npm install mongodb
# 或者使用 mongoose(ODM)
npm install mongoose
2. 数据库连接配置
使用原生 MongoDB 驱动
创建 lib/mongodb.js
文件:
import { MongoClient } from 'mongodb'
const uri = process.env.MONGODB_URI
const options = {}
let client
let clientPromise
if (process.env.NODE_ENV === 'development') {
// 开发环境中使用全局变量避免重复连接
if (!global._mongoClientPromise) {
client = new MongoClient(uri, options)
global._mongoClientPromise = client.connect()
}
clientPromise = global._mongoClientPromise
} else {
// 生产环境中每次创建新连接
client = new MongoClient(uri, options)
clientPromise = client.connect()
}
export default clientPromise
环境变量 .env.local
:
MONGODB_URI=mongodb://localhost:27017/myapp
# 或者 MongoDB Atlas
MONGODB_URI=mongodb+srv://username:password@cluster.mongodb.net/database
3. API 路由中的基本操作
创建基础的数据库操作函数
创建 lib/db.js
:
import clientPromise from './mongodb'
export async function connectToDatabase() {
const client = await clientPromise
const db = client.db('myapp') // 数据库名称
return { client, db }
}
4. 增加数据(Create)
插入单条记录
创建 pages/api/users/create.js
:
import { connectToDatabase } from '../../../lib/db'
export default async function handler(req, res) {
if (req.method !== 'POST') {
return res.status(405).json({ message: 'Method not allowed' })
}
try {
const { db } = await connectToDatabase()
const collection = db.collection('users')
// 插入单条记录
const newUser = {
name: req.body.name,
email: req.body.email,
age: req.body.age,
createdAt: new Date()
}
const result = await collection.insertOne(newUser)
res.status(201).json({
success: true,
insertedId: result.insertedId,
data: { ...newUser, _id: result.insertedId }
})
} catch (error) {
res.status(500).json({ success: false, error: error.message })
}
}
使用示例:
// 前端调用
const createUser = async () => {
const response = await fetch('/api/users/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'John Doe',
email: 'john@example.com',
age: 30
})
})
const result = await response.json()
console.log(result)
}
插入多条记录
创建 pages/api/users/create-many.js
:
import { connectToDatabase } from '../../../lib/db'
export default async function handler(req, res) {
if (req.method !== 'POST') {
return res.status(405).json({ message: 'Method not allowed' })
}
try {
const { db } = await connectToDatabase()
const collection = db.collection('users')
// 插入多条记录
const users = req.body.users.map(user => ({
...user,
createdAt: new Date()
}))
const result = await collection.insertMany(users)
res.status(201).json({
success: true,
insertedCount: result.insertedCount,
insertedIds: result.insertedIds
})
} catch (error) {
res.status(500).json({ success: false, error: error.message })
}
}
5. 查询数据(Read)
查询所有记录
创建 pages/api/users/index.js
:
import { connectToDatabase } from '../../../lib/db'
export default async function handler(req, res) {
if (req.method !== 'GET') {
return res.status(405).json({ message: 'Method not allowed' })
}
try {
const { db } = await connectToDatabase()
const collection = db.collection('users')
// 查询所有记录
const users = await collection.find({}).toArray()
res.status(200).json({
success: true,
count: users.length,
data: users
})
} catch (error) {
res.status(500).json({ success: false, error: error.message })
}
}
查询单条记录
创建 pages/api/users/[id].js
:
import { connectToDatabase } from '../../../lib/db'
import { ObjectId } from 'mongodb'
export default async function handler(req, res) {
const { id } = req.query
if (req.method === 'GET') {
try {
const { db } = await connectToDatabase()
const collection = db.collection('users')
// 通过 _id 查询单条记录
const user = await collection.findOne({ _id: new ObjectId(id) })
if (!user) {
return res.status(404).json({ success: false, message: 'User not found' })
}
res.status(200).json({
success: true,
data: user
})
} catch (error) {
res.status(500).json({ success: false, error: error.message })
}
}
}
条件查询
创建 pages/api/users/search.js
:
import { connectToDatabase } from '../../../lib/db'
export default async function handler(req, res) {
if (req.method !== 'GET') {
return res.status(405).json({ message: 'Method not allowed' })
}
try {
const { db } = await connectToDatabase()
const collection = db.collection('users')
const { name, email, minAge, maxAge, page = 1, limit = 10 } = req.query
// 构建查询条件
let query = {}
// 模糊查询名称
if (name) {
query.name = { $regex: name, $options: 'i' }
}
// 精确查询邮箱
if (email) {
query.email = email
}
// 年龄范围查询
if (minAge || maxAge) {
query.age = {}
if (minAge) query.age.$gte = parseInt(minAge)
if (maxAge) query.age.$lte = parseInt(maxAge)
}
// 分页
const skip = (parseInt(page) - 1) * parseInt(limit)
// 执行查询
const users = await collection
.find(query)
.sort({ createdAt: -1 }) // 按创建时间倒序
.skip(skip)
.limit(parseInt(limit))
.toArray()
// 获取总数
const total = await collection.countDocuments(query)
res.status(200).json({
success: true,
data: users,
pagination: {
page: parseInt(page),
limit: parseInt(limit),
total,
pages: Math.ceil(total / parseInt(limit))
}
})
} catch (error) {
res.status(500).json({ success: false, error: error.message })
}
}
查询特定字段
创建 pages/api/users/names.js
:
import { connectToDatabase } from '../../../lib/db'
export default async function handler(req, res) {
if (req.method !== 'GET') {
return res.status(405).json({ message: 'Method not allowed' })
}
try {
const { db } = await connectToDatabase()
const collection = db.collection('users')
// 只查询 name 和 email 字段,排除 _id
const users = await collection
.find({}, { projection: { name: 1, email: 1, _id: 0 } })
.toArray()
res.status(200).json({
success: true,
data: users
})
} catch (error) {
res.status(500).json({ success: false, error: error.message })
}
}
6. 更新数据(Update)
更新单条记录
在 pages/api/users/[id].js
中添加 PUT 方法:
import { connectToDatabase } from '../../../lib/db'
import { ObjectId } from 'mongodb'
export default async function handler(req, res) {
const { id } = req.query
if (req.method === 'PUT') {
try {
const { db } = await connectToDatabase()
const collection = db.collection('users')
const updateData = {
...req.body,
updatedAt: new Date()
}
// 移除不能更新的字段
delete updateData._id
delete updateData.createdAt
// 更新单条记录
const result = await collection.updateOne(
{ _id: new ObjectId(id) },
{ $set: updateData }
)
if (result.matchedCount === 0) {
return res.status(404).json({ success: false, message: 'User not found' })
}
// 返回更新后的数据
const updatedUser = await collection.findOne({ _id: new ObjectId(id) })
res.status(200).json({
success: true,
modifiedCount: result.modifiedCount,
data: updatedUser
})
} catch (error) {
res.status(500).json({ success: false, error: error.message })
}
}
}
更新多条记录
创建 pages/api/users/update-many.js
:
import { connectToDatabase } from '../../../lib/db'
export default async function handler(req, res) {
if (req.method !== 'PUT') {
return res.status(405).json({ message: 'Method not allowed' })
}
try {
const { db } = await connectToDatabase()
const collection = db.collection('users')
const { filter, update } = req.body
// 批量更新记录
const result = await collection.updateMany(
filter,
{
$set: {
...update,
updatedAt: new Date()
}
}
)
res.status(200).json({
success: true,
matchedCount: result.matchedCount,
modifiedCount: result.modifiedCount
})
} catch (error) {
res.status(500).json({ success: false, error: error.message })
}
}
使用示例:
// 将所有年龄小于18的用户状态设为未成年
const updateMinors = async () => {
const response = await fetch('/api/users/update-many', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
filter: { age: { $lt: 18 } },
update: { status: 'minor' }
})
})
const result = await response.json()
console.log(result)
}
7. 删除数据(Delete)
删除单条记录
在 pages/api/users/[id].js
中添加 DELETE 方法:
export default async function handler(req, res) {
const { id } = req.query
if (req.method === 'DELETE') {
try {
const { db } = await connectToDatabase()
const collection = db.collection('users')
// 删除单条记录
const result = await collection.deleteOne({ _id: new ObjectId(id) })
if (result.deletedCount === 0) {
return res.status(404).json({ success: false, message: 'User not found' })
}
res.status(200).json({
success: true,
deletedCount: result.deletedCount,
message: 'User deleted successfully'
})
} catch (error) {
res.status(500).json({ success: false, error: error.message })
}
}
}
删除多条记录
创建 pages/api/users/delete-many.js
:
import { connectToDatabase } from '../../../lib/db'
export default async function handler(req, res) {
if (req.method !== 'DELETE') {
return res.status(405).json({ message: 'Method not allowed' })
}
try {
const { db } = await connectToDatabase()
const collection = db.collection('users')
const { filter } = req.body
// 批量删除记录
const result = await collection.deleteMany(filter)
res.status(200).json({
success: true,
deletedCount: result.deletedCount,
message: `${result.deletedCount} users deleted successfully`
})
} catch (error) {
res.status(500).json({ success: false, error: error.message })
}
}
8. 高级查询操作
聚合查询
创建 pages/api/users/statistics.js
:
import { connectToDatabase } from '../../../lib/db'
export default async function handler(req, res) {
if (req.method !== 'GET') {
return res.status(405).json({ message: 'Method not allowed' })
}
try {
const { db } = await connectToDatabase()
const collection = db.collection('users')
// 聚合查询示例
const statistics = await collection.aggregate([
{
$group: {
_id: null,
totalUsers: { $sum: 1 },
averageAge: { $avg: '$age' },
minAge: { $min: '$age' },
maxAge: { $max: '$age' }
}
}
]).toArray()
// 按年龄分组统计
const ageGroups = await collection.aggregate([
{
$group: {
_id: {
$switch: {
branches: [
{ case: { $lt: ['$age', 18] }, then: 'minor' },
{ case: { $lt: ['$age', 65] }, then: 'adult' },
{ case: { $gte: ['$age', 65] }, then: 'senior' }
],
default: 'unknown'
}
},
count: { $sum: 1 }
}
}
]).toArray()
res.status(200).json({
success: true,
data: {
statistics: statistics[0],
ageGroups
}
})
} catch (error) {
res.status(500).json({ success: false, error: error.message })
}
}
文本搜索
创建 pages/api/users/text-search.js
:
import { connectToDatabase } from '../../../lib/db'
export default async function handler(req, res) {
if (req.method !== 'GET') {
return res.status(405).json({ message: 'Method not allowed' })
}
try {
const { db } = await connectToDatabase()
const collection = db.collection('users')
const { q } = req.query // 搜索关键词
if (!q) {
return res.status(400).json({ success: false, message: 'Search query required' })
}
// 首先需要创建文本索引
// await collection.createIndex({ name: 'text', email: 'text' })
// 执行文本搜索
const users = await collection
.find({ $text: { $search: q } })
.sort({ score: { $meta: 'textScore' } })
.toArray()
res.status(200).json({
success: true,
query: q,
count: users.length,
data: users
})
} catch (error) {
res.status(500).json({ success: false, error: error.message })
}
}
9. 在组件中使用
React 组件示例
// components/UserList.js
import { useState, useEffect } from 'react'
export default function UserList() {
const [users, setUsers] = useState([])
const [loading, setLoading] = useState(true)
const [newUser, setNewUser] = useState({ name: '', email: '', age: '' })
// 获取用户列表
const fetchUsers = async () => {
try {
const response = await fetch('/api/users')
const result = await response.json()
if (result.success) {
setUsers(result.data)
}
} catch (error) {
console.error('Error fetching users:', error)
} finally {
setLoading(false)
}
}
// 创建用户
const createUser = async (e) => {
e.preventDefault()
try {
const response = await fetch('/api/users/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newUser)
})
const result = await response.json()
if (result.success) {
setUsers([...users, result.data])
setNewUser({ name: '', email: '', age: '' })
}
} catch (error) {
console.error('Error creating user:', error)
}
}
// 删除用户
const deleteUser = async (id) => {
if (!confirm('确定要删除这个用户吗?')) return
try {
const response = await fetch(`/api/users/${id}`, {
method: 'DELETE'
})
const result = await response.json()
if (result.success) {
setUsers(users.filter(user => user._id !== id))
}
} catch (error) {
console.error('Error deleting user:', error)
}
}
useEffect(() => {
fetchUsers()
}, [])
if (loading) return <div>Loading...</div>
return (
<div>
<h2>User Management</h2>
{/* 创建用户表单 */}
<form onSubmit={createUser}>
<input
type="text"
placeholder="Name"
value={newUser.name}
onChange={(e) => setNewUser({...newUser, name: e.target.value})}
required
/>
<input
type="email"
placeholder="Email"
value={newUser.email}
onChange={(e) => setNewUser({...newUser, email: e.target.value})}
required
/>
<input
type="number"
placeholder="Age"
value={newUser.age}
onChange={(e) => setNewUser({...newUser, age: parseInt(e.target.value)})}
required
/>
<button type="submit">Create User</button>
</form>
{/* 用户列表 */}
<div>
{users.map(user => (
<div key={user._id} style={{ border: '1px solid #ccc', margin: '10px', padding: '10px' }}>
<h3>{user.name}</h3>
<p>Email: {user.email}</p>
<p>Age: {user.age}</p>
<button onClick={() => deleteUser(user._id)}>Delete</button>
</div>
))}
</div>
</div>
)
}
10. 错误处理和最佳实践
统一错误处理
创建 lib/errorHandler.js
:
export function handleApiError(error, res) {
console.error('API Error:', error)
if (error.name === 'MongoServerError') {
if (error.code === 11000) {
// 重复键错误
return res.status(400).json({
success: false,
error: 'Duplicate key error',
details: error.keyValue
})
}
}
if (error.name === 'CastError') {
// ObjectId 格式错误
return res.status(400).json({
success: false,
error: 'Invalid ID format'
})
}
// 通用错误
return res.status(500).json({
success: false,
error: 'Internal server error'
})
}
数据验证
创建 lib/validation.js
:
export function validateUser(userData) {
const errors = []
if (!userData.name || userData.name.trim().length < 2) {
errors.push('Name must be at least 2 characters long')
}
if (!userData.email || !isValidEmail(userData.email)) {
errors.push('Valid email is required')
}
if (!userData.age || userData.age < 0 || userData.age > 150) {
errors.push('Age must be between 0 and 150')
}
return {
isValid: errors.length === 0,
errors
}
}
function isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
return emailRegex.test(email)
}
这个完整指南涵盖了 Next.js 中使用 MongoDB 的所有基本操作,包括:
- 数据库连接配置 - 如何正确配置和管理数据库连接
- 增删改查操作 - 完整的 CRUD 操作示例
- 条件查询 - 各种查询条件和过滤器的使用
- 字段选择 - 如何只查询需要的字段
- 分页和排序 - 处理大量数据的最佳实践
- 聚合查询 - 复杂的数据统计和分析
- 错误处理 - 统一的错误处理机制
- 前端集成 - 在 React 组件中如何使用这些 API
每个示例都包含了详细的注释说明,可以根据实际需求进行修改和扩展。