If you’re working with Node.js and Express, you might have encountered Mongoose, a popular ODM (Object Data Modeling) library for MongoDB. While Mongoose provides a lot of helpful features, there are reasons you might choose to work directly with MongoDB's native drivers. In this post, I'll walk you through the benefits of using MongoDB native drivers and share how you can implement a simple CRUD API with them.
Performance: MongoDB native drivers offer better performance by directly interacting with MongoDB without the additional abstraction layer that Mongoose introduces. This can be particularly beneficial for high-performance applications.
Flexibility: Native drivers provide greater control over your queries and data interactions. Mongoose, with its schemas and models, imposes some structure, which might not be ideal for every use case.
Reduced Overhead: By using native drivers, you avoid the additional overhead of maintaining Mongoose schemas and models, which can simplify your codebase.
Learning Opportunity: Working directly with native drivers helps you understand MongoDB’s operations better and can be a great learning experience.
Here’s a step-by-step guide on how to set up a simple CRUD API using Node.js, Express, and MongoDB native drivers.
Create a utils/db.js file to manage the MongoDB connection:
require('dotenv').config() const dbConfig = require('../config/db.config'); const { MongoClient } = require('mongodb'); const client = new MongoClient(dbConfig.url); let _db; let connectPromise; async function connectToDb() { if (!connectPromise) { connectPromise = new Promise(async (resolve, reject) => { try { await client.connect(); console.log('Connected to the database ?', client.s.options.dbName); _db = client.db(); resolve(_db); } catch (error) { console.error('Error connecting to the database:', error); reject(error); } }); } return connectPromise; } function getDb() { if (!_db) { throw new Error('Database not connected'); } return _db; } function isDbConnected() { return Boolean(_db); } module.exports = { connectToDb, getDb, isDbConnected };
Create a models/model.js file to interact with the MongoDB collection:
const { ObjectId } = require('mongodb'); const db = require('../utils/db'); class AppModel { constructor($collection) { this.collection = null; (async () => { if (!db.isDbConnected()) { console.log('Waiting for the database to connect...'); await db.connectToDb(); } this.collection = db.getDb().collection($collection); console.log('Collection name:', $collection); })(); } async find() { return await this.collection.find().toArray(); } async findOne(condition = {}) { const result = await this.collection.findOne(condition); return result || 'No document Found!'; } async create(data) { data.createdAt = new Date(); data.updatedAt = new Date(); let result = await this.collection.insertOne(data); return `${result.insertedId} Inserted successfully`; } async update(id, data) { let condition = await this.collection.findOne({ _id: new ObjectId(id) }); if (condition) { const result = await this.collection.updateOne({ _id: new ObjectId(id) }, { $set: { ...data, updatedAt: new Date() } }); return `${result.modifiedCount > 0} Updated successfully!`; } else { return `No document found with ${id}`; } } async deleteOne(id) { const condition = await this.collection.findOne({ _id: new ObjectId(id) }); if (condition) { const result = await this.collection.deleteOne({ _id: new ObjectId(id) }); return `${result.deletedCount > 0} Deleted successfully!`; } else { return `No document found with ${id}`; } } } module.exports = AppModel;
Create an index.js file to define your API routes:
const express = require('express'); const router = express.Router(); const users = require('../controllers/userController'); router.get("/users", users.findAll); router.post("/users", users.create); router.put("/users", users.update); router.get("/users/:id", users.findOne); router.delete("/users/:id", users.deleteOne); module.exports = router;
Create a userController.js file to handle your CRUD operations:
const { ObjectId } = require('mongodb'); const Model = require('../models/model'); const model = new Model('users'); let userController = { async findAll(req, res) { model.find() .then(data => res.send(data)) .catch(err => res.status(500).send({ message: err.message })); }, async findOne(req, res) { let condition = { _id: new ObjectId(req.params.id) }; model.findOne(condition) .then(data => res.send(data)) .catch(err => res.status(500).send({ message: err.message })); }, create(req, res) { let data = req.body; model.create(data) .then(data => res.send(data)) .catch(error => res.status(500).send({ message: error.message })); }, update(req, res) { let id = req.body._id; let data = req.body; model.update(id, data) .then(data => res.send(data)) .catch(error => res.status(500).send({ message: error.message })); }, deleteOne(req, res) { let id = new ObjectId(req.params.id); model.deleteOne(id) .then(data => res.send(data)) .catch(error => res.status(500).send({ message: error.message })); } } module.exports = userController;
Store your MongoDB connection string in a .env file and create a db.config.js file to load it:
module.exports = { url: process.env.DB_CONFIG, };
Switching from Mongoose to MongoDB native drivers can be a strategic choice for certain projects, offering performance benefits and greater flexibility. The implementation provided here should give you a solid foundation to start building applications with Node.js and MongoDB native drivers.
Feel free to check out the full code on GitHub and explore more features or enhancements for your own projects!
Any further questions feel free to comment.
Happy coding! ?
The above is the detailed content of Building a Fast and Flexible CRUD API with Node.js and MongoDB Native Drivers. For more information, please follow other related articles on the PHP Chinese website!