Menu

MongoDB với Node.js: ví dụ driver find, insert, update

Dùng MongoDB từ Node.js với driver mongodb chính thức: kết nối bằng MongoClient, rồi thêm, tìm, cập nhật và xóa document trong một collection, tất cả bằng async/await.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Để dùng MongoDB từ Node.js, hãy cài driver chính thức (npm install mongodb), kết nối bằng new MongoClient(uri), chọn database bằng client.db('name') và collection bằng db.collection('name'). Mọi thao tác (insertOne, find, updateOne, deleteOne) đều trả về promise, nên bạn await chúng. Document được ghi vào và đọc ra dưới dạng object JavaScript thuần.

Chương trình in ra [ 'Dune', 'Neuromancer', 'Hyperion' ]. Database shop và collection books trước đó chưa tồn tại: MongoDB tạo chúng ở lần thêm đầu tiên, và toArray() nạp mọi document mà cursor của find() trỏ tới.

Tìm document với bộ lọc và projection

Bộ lọc (filter) là một object mô tả các document bạn muốn. Giá trị thường thì khớp chính xác; các toán tử như $gt, $lt, $in và $regex nằm trong một object lồng bên trong. projection ở đối số thứ hai chọn những trường nào được trả về.

recent chứa hai cuốn sách mới nhất sau năm 1960, Hyperion rồi đến Neuromancer, chỉ với title và year; findOne trả về kết quả khớp đầu tiên (hoặc null), và countDocuments đếm được 2 cuốn có giá từ 10 trở lên. Đổi $gt: 1960 thành $gt: 1985 thì chỉ còn lại Hyperion.

Cập nhật và xóa

updateOne(filter, update) thay đổi document đầu tiên khớp bộ lọc. Phần cập nhật dùng toán tử: $set thay giá trị các trường, $inc cộng thêm vào một số, $unset xóa một trường và $push thêm phần tử vào cuối mảng. updateMany và deleteMany áp dụng cho mọi document khớp.

Lệnh cập nhật khớp và sửa một document, nên giờ Dune có giá 10 và còn 2 cuốn trong kho, còn việc xóa cuốn sách đã hết hàng để lại 1 cuốn. Lệnh cập nhật bắt buộc dùng toán tử: truyền { price: 10 } mà không có $set thì driver từ chối (replaceOne mới là lệnh thay thế cả document).

ObjectId và index

Mỗi document đều có một _id; khi bạn không tự đặt, driver sẽ tạo một ObjectId. Để tìm document theo một id đến dưới dạng chuỗi (ví dụ từ URL), hãy bọc nó trong new ObjectId(id). Index giúp tìm kiếm theo một trường nhanh hơn, còn unique index khiến MongoDB từ chối giá trị trùng lặp.

Dưới dạng chuỗi, một ObjectId gồm 24 ký tự hex, và tìm bằng chuỗi thường thì không thấy gì, nên dòng đó in ra null. Lần thêm thứ hai vi phạm unique index và thất bại với mã 11000, đây là cách bạn phát hiện "email đã được dùng".

Dùng MongoDB trong một API Express

Kết nối một lần khi server khởi động rồi dùng chung collection; đừng mở kết nối mới cho mỗi request. Driver giữ một pool kết nối và dùng lại chúng.

GET /books trả về cả hai cuốn sách, mỗi cuốn có một _id. Sao chép một id vào GET /books/<id> để lấy một cuốn, hoặc gửi POST /books với body JSON như {"title": "Foundation", "year": 1951} rồi liệt kê lại danh sách sách.

App thật đọc connection string từ process.env.MONGODB_URI thay vì viết thẳng vào code.

Nếu bạn muốn có schema và validation bên trên driver, Mongoose (npm install mongoose) sẽ bọc nó lại:

const Book = mongoose.model('Book', new mongoose.Schema({ title: String, year: Number }));
await Book.create({ title: 'Dune', year: 1965 });

Lỗi thường gặp

Quên toArray(). find() trả về một cursor, không phải các document. In nó ra chỉ thấy một object cursor; hãy gọi toArray(), hoặc duyệt nó bằng for await.

Kết nối ở mỗi request. Mỗi new MongoClient mở một pool riêng. Hãy tạo một client lúc khởi động và dùng lại ở mọi nơi.

Không bao giờ đóng client trong script. Một client còn mở giữ tiến trình chạy tiếp sau khi code của bạn đã xong. Gọi await client.close() ở cuối script; còn server thì giữ nó mở suốt thời gian chạy.

Câu hỏi thường gặp

Nên dùng driver mongodb hay Mongoose?

Driver là lớp chính thức và mỏng hơn: bạn làm việc với object thuần và ngôn ngữ truy vấn riêng của MongoDB. Mongoose thêm schema, validation và model bên trên, hữu ích khi bạn muốn mọi document đều được kiểm tra. Cả hai làm việc với cùng một cơ sở dữ liệu, và bên dưới Mongoose cũng dùng driver.

Có cần tạo database hoặc collection trước không?

Không. MongoDB tạo cả hai ở lần đầu tiên bạn thêm document vào. client.db('shop') và db.collection('books') chỉ trả về handle để thao tác.

Nên đặt connection string ở đâu?

Trong một biến môi trường như MONGODB_URI, đọc bằng process.env.MONGODB_URI. Connection string của database được host sẵn có chứa mật khẩu, nên tuyệt đối không được commit vào repository.

Vì sao find không trả về document nào khi tìm theo id?

_id được lưu dưới dạng ObjectId, không phải chuỗi, nên { _id: '65f1...' } không khớp gì cả. Hãy chuyển đổi trước: { _id: new ObjectId(id) }.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu