Working with Node.js Entities and Mongoose Models - II

chiranjib_b

Chiranjib

Posted on February 19, 2023

Working with Node.js Entities and Mongoose Models - II

Previous: Working with Node.js Entities and Mongoose Models - I

The finish line is very close now, let's do it!

Step 1 - understand that the magic happens 'async'

Node.js is single-threaded and JavaScript is asynchronous by design. If you don't know or understand how it works, then I'd strongly advise that you should familiarize with the concepts of asynchronous processing first. Here's a good explanation of the concepts.

Step 2 - modify our controller to support asynchronous processing

In the file ./controllers/index.js, we need to make the router functions async, and remember to await before returning the response:

const BaseEntity = require('_entities').getBaseEntity();
const logger = require('_utils/logger');

/**
 * 
 * @param {BaseEntity} entityObject 
 * @returns {[string, Router]}
 */
function getController(entityObject) {
    const router = require('express').Router({ mergeParams: true });
    router.post('/', async (req, res, next) => {
        try {
            const response = await entityObject.create(req.body);
            res.json(response);
        } catch(e) {
            logger.error(e);
            res.status(500).send('Oops! Something went wrong!');
        }
    });

    router.get('/', async (req, res, next) => {
        try {
            const response = await entityObject.read();
            res.json(response);
        } catch(e) {
            logger.error(e);
            res.status(500).send('Oops! Something went wrong!');
        }
    });

    router.get('/:entityObjectId', async (req, res, next) => {
        try {
            const response = await entityObject.read(req.params.entityObjectId);
            res.json(response);
        } catch(e) {
            logger.error(e);
            res.status(500).send('Oops! Something went wrong!');
        }
    });

    router.put('/:entityObjectId', async (req, res, next) => {
        try {
            const response = await entityObject.update(req.params.entityObjectId, req.body);
            res.json(response);
        } catch(e) {
            logger.error(e);
            res.status(500).send('Oops! Something went wrong!');
        }
    });

    router.delete('/:entityObjectId', async (req, res, next) => {
        try {
            const response = await entityObject.delete(req.params.entityObjectId);
            res.json(response);
        } catch(e) {
            logger.error(e);
            res.status(500).send('Oops! Something went wrong!');
        }
    });

    return [`/${entityObject.name}`, router];
}

module.exports = {
    getController
};
Enter fullscreen mode Exit fullscreen mode

Step 3 - Create entities for Movie and User

./entities/movie/index.js
const BaseEntity = require('_entities/BaseEntity');

module.exports = class MovieEntity extends BaseEntity {

    constructor() {
        super();
        this.name = 'Movie';
        this.model = require('_data-access/models/Movie');
    }

    async create(payload) {
        return await this.model.create(payload);
    }

    async read(id) {
        if (id) {
            return await this.model.findById(id).lean();
        } else {
            return await this.model.find().lean();
        }
    }

    async update(id, payload) {
        return await this.model.findByIdAndUpdate(id, payload, { new: true });
    }
};

Enter fullscreen mode Exit fullscreen mode
./entities/user/index.js
const BaseEntity = require('_entities/BaseEntity');

module.exports = class UserEntity extends BaseEntity {
    constructor() {
        super();
        this.name = 'User';
        this.model = require('_data-access/models/User');
    }

    async create(payload) {
        return await this.model.create(payload);
    }

    async read(id) {
        if (id) {
            return await this.model.findById(id).lean();
        } else {
            return await this.model.find().lean();
        }
    }

    async update(id, payload) {
        return await this.model.findByIdAndUpdate(id, payload, { new: true });
    }
};

Enter fullscreen mode Exit fullscreen mode

Take note, both the entities are not supporting the delete method, so the expectation is that if someone (could be a hacker) hits the DELETE endpoint, they would see that it is not supported.

Step 4 - generate controllers and expose them for the new entities

In our ./index.js file, we have to make modifications to the setupApp function as follows:

....

    app.use(...getController(entities.getEntityOne()));
    app.use(...getController(entities.getMovieEntity()));
    app.use(...getController(entities.getUserEntity()));

    app.use('/', (req, res) => {
        res.send(`${req.originalUrl} can not be served`);
    });
....
Enter fullscreen mode Exit fullscreen mode

And that's it! We have a fully functional persistent Node.js backend.

Next: Working with Node.js Entities and Mongoose Models - III

💖 💪 🙅 🚩
chiranjib_b
Chiranjib

Posted on February 19, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related