MongoDB 简介

在 Node.js 中操作 MongoDB

学会用官方 mongodb 驱动在 Node.js 中连接数据库并完成增删改查,打通前后端数据链路。

🎯 引言

前面几篇我们都是在 mongosh 里手写命令操作数据库。实际项目里,这些操作是由后端代码执行的:用户发起请求,Node.js 服务连上 MongoDB,查出数据再返回给浏览器。

学完这篇文章,你能用官方 mongodb 驱动在 Node.js 中连接 MongoDB,写出完整的增删改查代码,并结合 Express 提供一个返回用户列表的接口。这是把数据库知识和后端开发真正打通的关键一步。


🧱 安装与准备

mongodb 是 MongoDB 官方推出的 Node.js 驱动,提供了连接数据库、操作集合的全部能力,由官方团队维护,是 Node.js 生态里访问 MongoDB 的常用选择。

本文及本课程基于 Node.js v22 LTSmongodb 7.x(官方驱动)编写和验证。新建一个项目目录,初始化后安装:

mkdir node-mongo && cd node-mongo
npm init -y
npm install mongodb

在开始写代码前,先确认 MongoDB 容器还在运行:

docker ps

如果列表里没有 mongo-demo,用 docker start mongo-demo 把它启动起来。

本文所有示例都围绕 blog_demo 数据库的 user 集合展开,字段保持 usernameagecity 不变。如果集合里没有数据,可以先在 mongosh 里插入几条,也可以直接用后面的代码新增。

✨ 用 MongoClient 连接数据库

连接 MongoDB 需要一个连接串,格式像网址一样,依次写清楚协议、账号密码、地址和端口:

mongodb://root:123456@localhost:27017

代码里用 MongoClient 建立连接,再依次拿到数据库和集合:

db.js
const { MongoClient } = require('mongodb');

const url = 'mongodb://root:123456@localhost:27017';
const client = new MongoClient(url);

async function main() {
    try {
        await client.connect();
        console.log('连接成功');

        const db = client.db('blog_demo');
        const user = db.collection('user');

        // 后续操作都基于 user 这个集合
    } finally {
        await client.close();
    }
}

main();

几个关键步骤:

  • new MongoClient(url):创建客户端实例,此时还没有真正连接。
  • await client.connect():发起连接,网络不通或账号密码错误会在这里抛错。
  • client.db('blog_demo'):选中数据库;db.collection('user'):选中集合。这两步只是拿到操作入口,不会真的访问数据库。
  • try/finally 保证无论中间是否出错,最后都会执行 client.close() 关闭连接,避免脚本卡住不退出。

可以打个比喻:client 像拨通电话,db 像找到对应的部门,collection 像找到部门里具体负责的那个人,找到人之后才能开始办事。

真实项目中密码不能写死在代码里,要放到环境变量中(比如用 .env 文件配合 process.env 读取),避免提交到代码仓库泄露。学习阶段写死即可,心里有这根弦就行。

🛠 完整的增删改查

下面这个 crud.js 覆盖了日常开发中的完整 CRUD:新增、查列表、按 id 查单个、更新、删除。

crud.js
const { MongoClient, ObjectId } = require('mongodb');

const url = 'mongodb://root:123456@localhost:27017';
const client = new MongoClient(url);

async function main() {
    try {
        await client.connect();
        const user = client.db('blog_demo').collection('user');

        // 新增用户
        const result = await user.insertOne({
            username: '小美',
            age: 22,
            city: '深圳',
        });
        console.log('新增成功,_id =', result.insertedId);

        // 查询用户列表
        const list = await user.find().toArray();
        console.log('用户列表:', list);

        // 按 id 查询单个用户
        const one = await user.findOne({ _id: new ObjectId(result.insertedId) });
        console.log('按 id 查询:', one);

        // 更新用户
        await user.updateOne(
            { _id: new ObjectId(result.insertedId) },
            { $set: { age: 23 } }
        );

        // 删除用户
        await user.deleteOne({ _id: new ObjectId(result.insertedId) });
    } finally {
        await client.close();
    }
}

main();

运行 node crud.js,就能看到新增、查询、更新、删除依次生效。

注意 ObjectId 的用法:MongoDB 的 _id 不是普通字符串,而是一个特殊的对象类型。从 mongodb 包里引入 ObjectId,用 new ObjectId(id字符串) 包一层才能按 id 查询,直接传字符串是查不到的。这是新手很容易踩的坑。

💡 结合 Express 写一个接口

最后把知识串起来,写一个简洁的 Express 接口:访问 GET /users 返回用户列表。Express 基于 5.x 版本,先安装:

npm install express
server.js
const express = require('express');
const { MongoClient } = require('mongodb');

const url = 'mongodb://root:123456@localhost:27017';
const client = new MongoClient(url);

const app = express();

app.get('/users', async (req, res) => {
    const user = client.db('blog_demo').collection('user');
    const list = await user.find().toArray();
    res.json(list);
});

async function main() {
    await client.connect();

    app.listen(3000, () => {
        console.log('服务已启动:http://localhost:3000');
    });
}

main();

和一次性脚本不同,服务是长期运行的,所以我们只在启动时连接一次数据库,之后每个请求都复用这个连接,不需要每次请求都 connect 和 close。

运行 node server.js,浏览器访问 http://localhost:3000/users,就能看到 JSON 格式的用户列表。到这里,浏览器、Node.js、MongoDB 这条完整链路就打通了。


🧾 小节总结

  • mongodb 是官方 Node.js 驱动,连接串格式为 mongodb://账号:密码@地址:端口
  • new MongoClient(url) 创建客户端,await client.connect() 建立连接,try/finally 保证 client.close() 一定执行。
  • 通过 client.db('blog_demo') 选数据库、db.collection('user') 选集合,拿到集合后即可操作。
  • insertOne 新增后用 result.insertedId 拿新文档的 _idfind().toArray() 查列表;findOne 查单个;updateOne 配合 $set 更新;deleteOne 删除。
  • _id 是 ObjectId 类型,按 id 查询要用 new ObjectId(id) 转换,不能直接传字符串。
  • Express 服务在启动时连接一次数据库,各请求复用连接,不要每次请求都重新连接。

❓ 知识问答

Q1:为什么脚本报错说连接被拒绝?

先检查 mongo-demo 容器是否在运行(docker ps),再确认连接串里的账号、密码、端口和启动容器时设置的一致。容器没启动或密码写错都会导致连接失败。

Q2:client.db()db.collection() 会访问数据库吗?

不会。这两步只是拿到操作入口,真正的网络请求发生在 connect()find()insertOne() 这些操作上。所以即使数据库或集合还不存在,这两行也不会报错。

Q3:为什么用字符串形式的 _id 查询返回 null?

MongoDB 的 _id 是 ObjectId 类型,和字符串不相等。要从 mongodb 包引入 ObjectId,写成 findOne({ _id: new ObjectId(id) })

Q4:每次操作完都要调用 client.close() 吗?

一次性脚本需要,否则进程不会退出。Express 这类长期运行的服务则相反,只在启动时连接一次并持续复用,进程关闭时才需要关闭连接。

Q5:密码写在代码里有什么风险?

代码一旦提交到 Git 仓库或被他人看到,数据库密码就泄露了。真实项目要用环境变量管理,并确保 .env 文件在 .gitignore 中。


🧪 小练习

练习一:参照本文示例,写一个 getUserByName 函数,接收用户名参数,返回对应的用户文档,查不到时返回 null

crud.js
const { MongoClient } = require('mongodb');

const url = 'mongodb://root:123456@localhost:27017';
const client = new MongoClient(url);

async function getUserByName(username) {
    // 请在这里编写代码
}

练习二:给 Express 服务再加一个 GET /users/:name 接口,根据路径中的用户名查询单个用户并返回 JSON。

server.js
app.get('/users/:name', async (req, res) => {
    // 请在这里编写代码
});

🎉 恭喜你已经掌握在 Node.js 中操作 MongoDB 的技能啦!下一篇我们学习 Mongoose,它能给集合加上字段约束和校验,让数据操作更规范、更省心。