Step by Step CRUD API using Node.js and Mongoose

Learn Step by Step CRUD API using Node.js and Mongoose. This tutorial covers step-by-step instructions for setting up the project, installing dependencies, creating the server, connecting to MongoDB.

Creating the Product model, creating the API routes, mounting the API routes, testing the API, writing Swagger documentation, and deploying to Docker Desktop.

Steps CRUD API using Node.js and Mongoose

Step 1: Setting up the project

Create a new directory for your project and navigate into it.

mkdir nodejs-mongoose-example
cd nodejs-mongoose-example

Step 2: Installing dependencies

Initialize npm in your project and install necessary packages.

npm init -y
npm install express mongoose body-parser swagger-jsdoc swagger-ui-express

Step 3: Creating the server

Create a file named server.js and set up an Express server.

app.post('/api/login', (req, res) => {

    // Query the database for the user
    usersCollection.findOne({
        username: req.body.username,
        password: req.body.password
    }, (err, user) => {
        if (err) {
            console.error('Error finding user:', err);
            res.status(500).send('Internal server error');
            return;
        }
        if (user) {
            // User found, send success response
            res.send('Login successful');
        }
    })
})
// server.js
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});

Run Server: follow below command to run server with node assuming nodemon is not setup in dev env

node server.js

Read Also : What is NoSQL Injection attack in nodejs

Step 4: Connecting to MongoDB

Create a MongoDB Atlas account and obtain the connection string. Replace the connection string in the following code.

// server.js
const mongoose = require('mongoose');

mongoose.connect('mongodb://<username>:<password>@<cluster-url>/<database-name>', {
  useNewUrlParser: true,
  useUnifiedTopology: true
})
.then(() => console.log('Connected to MongoDB'))
.catch(err => console.error('Error connecting to MongoDB', err));

Step 5: Creating the Product model

Create a models directory and inside it, create a file named product.js to define the product schema and model.

// models/product.js
const mongoose = require('mongoose');

const productSchema = new mongoose.Schema({
  name: String,
  price: Number,
  description: String
});

module.exports = mongoose.model('Product', productSchema);

Step 6: Creating the API routes

Create a routes directory and inside it, create a file named productRoutes.js to define the API routes for products.

// routes/productRoutes.js
const express = require('express');
const router = express.Router();
const productController = require('../controllers/productController');

router.post('/', productController.createProduct);
router.get('/:id', productController.getProductById);
router.put('/:id', productController.updateProduct);
router.delete('/:id', productController.deleteProduct);

module.exports = router;

Step 7: Mounting the API routes

Mount the product routes in the server.js file.

// server.js
const productRoutes = require('./routes/productRoutes');

app.use('/api/products', productRoutes);

Step 8: Creating the Product Controller for CRUD Operation

Create a controllers directory and inside it, create a file named productController.js to define the CRUD operations for products.

// controllers/productController.js
const Product = require('../models/product');

exports.createProduct = async (req, res) => {
  try {
    const product = await Product.create(req.body);
    res.status(201).json(product);
  } catch (err) {
    res.status(400).json({ error: err.message });
  }
};

// Implement other CRUD operations similar to createProduct

Step 9: Testing the API

let’s write some unit tests using the Jest framework to test the CRUD operations of our Node.js application with MongoDB using Mongoose. We’ll cover positive and negative test cases for each CRUD operation.

First, make sure you have Jest installed in your project:

npm install --save-dev jest supertest

Then, let’s create a tests directory in the root of your project. Inside this directory, create a file named product.test.js.

Read Also : 2 Powerful Testing Suits: sinon and chai using nodejs

// tests/product.test.js
const request = require('supertest');
const app = require('../server');

describe('Product API', () => {
  let productId;

  // Test POST /api/products
  describe('POST /api/products', () => {
    it('should create a new product', async () => {
      const res = await request(app)
        .post('/api/products')
        .send({ name: 'Test Product', price: 10, description: 'Test description' });

      expect(res.statusCode).toEqual(201);
      expect(res.body).toHaveProperty('_id');
      expect(res.body.name).toEqual('Test Product');
      productId = res.body._id;
    });
  });

  // Test GET /api/products/:id
  describe('GET /api/products/:id', () => {
    it('should fetch the created product', async () => {
      const res = await request(app)
        .get(`/api/products/${productId}`);

      expect(res.statusCode).toEqual(200);
      expect(res.body._id).toEqual(productId);
    });

    it('should return 404 if product not found', async () => {
      const res = await request(app)
        .get('/api/products/nonexistent-id');

      expect(res.statusCode).toEqual(404);
    });
  });

  // Test PUT /api/products/:id
  describe('PUT /api/products/:id', () => {
    it('should update the created product', async () => {
      const res = await request(app)
        .put(`/api/products/${productId}`)
        .send({ price: 20 });

      expect(res.statusCode).toEqual(200);
      expect(res.body.price).toEqual(20);
    });

    it('should return 404 if product not found', async () => {
      const res = await request(app)
        .put('/api/products/nonexistent-id')
        .send({ price: 20 });

      expect(res.statusCode).toEqual(404);
    });
  });

  // Test DELETE /api/products/:id
  describe('DELETE /api/products/:id', () => {
    it('should delete the created product', async () => {
      const res = await request(app)
        .delete(`/api/products/${productId}`);

      expect(res.statusCode).toEqual(200);
    });

    it('should return 404 if product not found', async () => {
      const res = await request(app)
        .delete('/api/products/nonexistent-id');

      expect(res.statusCode).toEqual(404);
    });
  });
});

In this test suite:

  • We use supertest to make HTTP requests to our API.
  • For each CRUD operation, we have positive test cases to ensure the operation succeeds as expected, and negative test cases to handle error scenarios (e.g., when a product is not found).
  • We’re expecting specific HTTP status codes for each operation.
  • We’re also checking the response body for specific properties or values.

To run the tests, you can use the following command:

npm test

This will execute the tests using Jest, and you should see the results in your console.

These tests ensure that your API endpoints are functioning correctly and handle error cases appropriately. They help maintain the reliability and stability of your application as you make changes or add new features.

Step 10: Writing Swagger documentation for create, fetch, update, delete

Create a file named swagger.js to define Swagger documentation for your API.

// swagger.js
const swaggerJSDoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express');

const options = {
  definition: {
    openapi: '3.0.0',
    info: {
      title: 'Node.js Mongoose API',
      version: '1.0.0',
      description: 'API for managing products with Node.js and Mongoose',
    },
  },
  apis: ['./routes/*.js'],
};

const specs = swaggerJSDoc(options);

module.exports = (app) => {
  app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs));
};

Mount the Swagger documentation in the server.js file.

// server.js
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

require('./swagger')(app); // Mount Swagger documentation

app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});

Step 11: Deploying to Docker Desktop

Create a Dockerfile in the root directory of your project.

# Dockerfile
FROM node:latest

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

EXPOSE 3000

CMD ["node", "server.js"]

Build your Docker image.

docker build -t node-mongoose-example .

Run your Docker container.

docker run -p 3000:3000 node-mongoose-example

Conclusion

We have successfully created a Node.js application with MongoDB using Mongoose, added Swagger documentation, and deployed it to Docker Desktop.

We can further enhance this application by adding authentication, validation, error handling, and more features as per your requirements.