Node.js Dev series (4 of 6)
In this post, we will focus on creating a microservice that interact with a NoSQL database. Since we've already built a service that connects to MySQL, developing a NoSQL-based service seems like a logical next step.
If you want to check out the full GitHub repository for this series, you can find it here: GitHub Repo.
Optimizing Our Docker Setup: Reusing the Dockerfile for Multiple Services
Currently, we have a Dockerfile in products service folder. If we follow this approach, each Node.js service will have its own Dockerfile. However, since all our Node.js services will use the same build process, we could just reuse a single Dockerfile instead of maintaining separate copies for each service.
1. Move the existing Dockerfile
2. Modify the Dockerfile to Accept a Build Argument
ARG SERVICE
COPY ./$SERVICE/package.json .
3. Update docker-compose.yml to Pass the Service Argument
products:
build:
context: ./services
args:
- SERVICE=products
That configuration should allow us to reuse the Dockerfile later. Let's get coding!
Setting Up the MongoDB Microservice
MongoDB is a popular, open-source NoSQL database that is designed for handling large volumes of unstructured or semi-structured data. Unlike traditional relational databases that store data in tables and rows, MongoDB uses a flexible, document-oriented approach. It stores data in BSON (Binary JSON) format, which is similar to JSON, allowing for rich, complex data structures like arrays and nested objects.
Since MongoDB is really popular, we’re going to create a new microservice that interacts with MongoDB. We already have a working structure in the products service, I'm going to show you how easily we can copy and modify most of that code and have the new service running in no time.
Inside the services directory, create a new folder called: ?services/suppliers
Modify package.json
Open services/suppliers/package.json and update the following:
Key Changes:
Now that we've set up the package.json, let's update the service to use suppliers instead of products.
Update app.js
In services/suppliers/app.js:
const supplierRoutes = require("./routes/suppliers");
app.use("/suppliers", supplierRoutes);
This ensures requests to /suppliers are routed correctly.
Update config.js
Modify services/suppliers/config.js to store MongoDB connection details:
We remove MySQL connection details, and specify MongoDB connection URL and database name.
Modify models/connection.js
Remove all existing code and replace it with:
Updating routes/suppliers.js to Handle MongoDB CRUD Operations
Now that we’ve set up MongoDB, the final step is to update routes/suppliers.js to work with it instead of MySQL.
1. Remove MySQL-Specific Code
2. Add MongoDB-Specific Imports
Replace the removed code with:
const { ObjectID } = require("mongodb");
const { getDb } = require("../models/connection");
Why these imports?
3. Implement the "Create Supplier" Endpoint
Since MongoDB uses collections and documents instead of tables and records, the logic is different.
Replace the existing creation logic with:
Key Differences from MySQL:
4. Implementing the Read Endpoints for Suppliers
Now that we’ve added supplier creation, let’s implement the read endpoints to retrieve supplier data from MongoDB.
4.1. Fetch All Suppliers
This endpoint retrieves all suppliers from the MongoDB collection.
How it works:
4.2. Fetch a Single Supplier by ID
This endpoint retrieves a specific supplier using the unique _id.
How it works:
MongoDB uses ObjectIDs, so we must convert id into an ObjectID before querying. If the id is not a valid ObjectID, MongoDB will throw an error.
5. Updating a Supplier by ID
This endpoint allows updating a supplier's details using their unique _id.
How it Works:
Why removeNullUndefined()?
This helper function ensures that only provided fields are updated. Without it, empty values might overwrite existing data as null or undefined. If a field is missing from the request, it won’t be modified. So let’s add removeNullUndefined helper function after the require declarations:
const removeNullUndefined = (obj) =>
Object.fromEntries(Object.entries(obj).filter(([_, v]) => v != null));
How It Works:
1. Object.entries(obj) Converts the object into an array of key-value pairs.
Example:
{ name: "Alice", contact: null }
turns into
[["name", "Alice"], ["contact", null]]
2. .filter(([_, v]) => v != null)
Keeps only the key-value pairs where the value is not null or undefined.
v != null is a shorthand for v !== null && v !== undefined.
Example result:
[["name", "Alice"]]
3. Object.fromEntries(...)
Converts the filtered array back into an object.
Example output:
{ name: "Alice" }
This shows how removed with the helper function all null or undefined properties from the object.
6. Deleting a supplier by Id
Step-by-Step Explanation
1. Extracting the ID from the request parameters
const { id } = req.params;
2. Converting the ID to MongoDB's ObjectID format
{ _id: new ObjectID(id) }
3. Executing the deleteOne query
db.collection("suppliers").deleteOne(
{ _id: new ObjectID(id) },
(err, result) => { ... }
);
4. Deletes only one document that matches the given _id.
5. Handling Errors
if (err) {
res.status(500).json({ error: "Failed to delete supplier" });
return;
}
6. If an error occurs (e.g., database issues), it returns HTTP 500 with an error message.
7. Returning a 204 No Content Response
res.status(204).send();
204 No Content means the request was successful, but there's no response body. This is the correct status code for a successful DELETE operation.
You might be thinking: This is a hoax! You just copy-pasted a bunch of code! And you're absolutely right—I did. A significant part of a developer’s work involves copy-pasting. Whether you write every line from scratch or copy and modify existing code, what truly matters is delivering solutions quickly and efficiently while maintaining quality.
There’s no shame in copy-pasting—as long as you know what you’re doing. The key is having the judgment to use the right code in the right context. If you blindly copy and paste random pieces of code without understanding them, you’re setting yourself up for major problems. It’s crucial to grasp the logic behind what you’re implementing, the nuances between different platforms, and the philosophies behind various technologies.
For instance, databases serve different purposes:
When you understand why technologies work the way they do, you can leverage any piece of code effectively. That’s why reading documentation, understanding best practices, and learning from resources like this tutorial are invaluable.
With that in mind—get comfortable with copy-pasting, because we’ll be doing plenty of it to avoid reinventing the wheel and focus on solving real problems efficiently.
Set Up MongoDB Fixtures
Before testing this, we need to set up database fixtures for MongoDB, just like we did for MySQL.
db.suppliers.insertMany([
{ name: "first 1", contact: "[email protected]" },
{ name: "second 2", contact: "627276543" },
]);
What This Script Does
Adding Services to docker-compose.yml
Now we’re at the final step before testing: adding MongoDB and our Node.js suppliers service to the docker-compose.yml file.
Add the MongoDB Service
What This Does:
Add the suppliers Node.js Service
Now we need to add the suppliers service to docker-compose.yml, ensuring it's properly configured to interact with MongoDB and supports hot-reloading for development.
New Settings:
Settings that we can also find in products:
o?? "./services/suppliers:/app:cached" → Mounts the local project directory into the container for live code updates.
o?? "/app/node_modules" → Ensures dependencies are properly managed inside the container.
Now, we can build and start everything.
Testing the Suppliers API with Postman
Now that our MongoDB-based suppliers service is running, it's time to test the API using Postman. Basically, you can duplicate products endpoints making sure to modify the necessary parameters.
Example API Requests
1. Create a Supplier
{
"name": "Tech Supplies Co.",
"contact": "[email protected]"
}
{
"acknowledged": true,
"insertedId": "65a9f12e8b5e1a6e4d2c9b1f"
}
2. Get All Suppliers
[
{
"_id": "65a9f12e8b5e1a6e4d2c9b1f",
"name": "Tech Supplies Co.",
"contact": "[email protected]"
}
]
3. Get a Supplier by ID
{
"_id": "65a9f12e8b5e1a6e4d2c9b1f",
"name": "Tech Supplies Co.",
"contact": "[email protected]"
}
4. Update a Supplier
{
"name": "Updated Tech Supplies Co.",
"contact": "[email protected]"
}
{
"matchedCount": 1,
"modifiedCount": 1,
"acknowledged": true
}
5. Delete a Supplier
Expected Response:204 No Content
What’s Next?
Next time, we’ll explore Amazon DynamoDB, a NoSQL database which also introduces us to AWS services. It’s a bit different from MongoDB, so I thought it would be worthwhile to explore in more detail. If you have any comments or suggestions please let me know.