How to create common CRUD operations in Express using Sequelize?

Viewed 548

I want to create common crud operations in Express js with Sequelize. I have created getAll function as below.

 exports.getAll = (module, res,next) => {   
    module
        .findAndCountAll({
            where: {
                CreatedBy: 1,
                isDeleted: false,
                isActive: true
            },
            offset: 0,
            limit: 10,
        }).then((result) => {
            res.status(200).json({
                message: "data Fetched from database",
                statusCode: 200,
                result: result,
            });
        }).catch((error) => {
            console.log(error);
        });
}

and I am calling this common function in Controller function as below by passing name of Model e.g. category

crudOperations.getAll(category, res);

It is working fine. but how do I create function for post data ? For posting data, I want to use Sequelize's magic methods (because one user can associated with many category as below)

user.hasMany(category, {
        foreignKey: 'CreatedBy'
    });

Example, I want to add category with respect to user, so I want to use magic method as below.

req.user
    .createCategory({
        name: name,
    })
  • How should I pass user and createCategory as parameter to common function?
  • How do I pass data to function?
  • Is it good practice to create common function for CRUD? or should go with writing function for each module?
1 Answers

I am building application architecture and design. When we are talking about creating common methods or services, it would completely depend on the use case or types of operation we are performing.

Sequelize has already basic methods which perform common operations. Following is my idea which might put more light on your way. Please refer to below pseudo-code.

Answer 1.

Following is the method that might help you, that how do you organize your functions. It is my basic thought, so there might room for an error.

BaseModel.helper.js

class BaseModelHelper{
    
    static async find(params){
        const {model, where, attributes, include=[] offset:0, limit: 10} = params;
        objFind = {};
        if(Object.keys(where).length > 0){
            objFind = {...objFind, where}
        }   
        if(attributes.length > 0){
            objFind = {...objFind, attributes}
        }  
        if(include.length > 0){
            objFind = {...objFind, include}
        }
        model.find({
            where: where,
            attributes: attributes,
            include,
            offset,
            limit
        }).then((data)=>{
            return data;
        }).catch((err)=>{
            throw new Error(err);
        });
    }

    static async create(params){
        const {model, properties} = params;
        model.create(properties)
        .then((data)=>{
            return data;
        }).catch((err)=>{
            throw new Error(err);
        });
    }


    static async update(params){
        const {model, newData, where} = params;
        model.update(newData, {
            where
        })
        .then((data)=>{
            return data;
        }).catch((err)=>{
            throw new Error(err);
        });
    }

    static async delete(params){
        const {model, where} = params;
        model.destroy(where)
        .then((data)=>{
            return data > 0 ? true : false;
        }).catch((err)=>{
            throw new Error(err);
        });
    }

}

module.exports = BaseModelHelper;

Data.services.js

const BaseModelHelper = require("BaseModel.helper.js);

class DataServices{    

    static async add(){
        // Owner table entry
        const {id} = await BaseModelHelper.create({
            model: "Owner'
            properties:{
                name: 'Loren',
                role: 'admin',
            },            
        });
        // Cat table entry
        const {id: petId} = await BaseModelHelper.create({
            model: "Pet'
            properties:{
                owner_id: 'c0eebc45-9c0b',                
                type: 'cat',
            } 
        });
    }


    static async findWithRelations(){
        const arrData = await BaseModelHelper.find({
            model: 'User',
            where: {
                role: 'admin'
            },
            attributes: ['id', 'username', 'age'],
            include: [{
                model: pet,
                through: {
                    attributes: ['createdAt', 'startedAt', 'finishedAt'],
                    where: {completed: true}
                }
            }]
        });
    }


    static async findBelongs(){
        const arrData = await BaseModelHelper.find({
            model: 'User',
            where: {
                role: 'admin'
            },
            attributes: ['id', 'username', 'age']
        });
    }


    static async update(){        
        const arrData = await BaseModelHelper.update({
            Model: 'Pet',
            newData:{
                name: 'lina',
            }
            where: {
                name: 'suzi',
            }
        });
    }

    static async delete(){
        const arrData = await BaseModelHelper.delete({
            Model: 'Pet',
            where: {
                name: 'lina',
            }
        });
    }

    
}

module.exports = DataServices;

The above way I have described has one benefit is to you don't go to do error handling every place, if you have to manage to centralize error handler. An when an error occurred it throws an error and catches by centralizing the error handler of Express.

The above class of common db operation is build based on my experience and the frequency of the operations we have performed. I know that there is room for more possibilities than we expect, but with the method, I have suggested you might not face many obstacles.

Answer 2. You should create a whole qualified query object at your service level. Once its build at the service level, then only you will pass it to our BaseModelHelper.

Answer 3. Same thing you should create your data object at the service level. If the data object builds from multiple tables, then you first encapsulate at the service level, then you should pass to the BaseModelHelper method.

Answer 4. Yes, I also favor the same. But one thing you should keep in mind that there is always room for improvement. If you wish to create a method for each module then you should copy this BaseModelHelper to everywhere else I suggest inheriting the file at the service level.

All the database operation objects build at the service level, not the controller level. Your database service and object preparation services might be different so it will give a more clear picture. The object preparation service might scope to include more different services.

Again above approach is my thought process and I suggested you based on my experience. Again there is more room for improvement, that you might take to create.

Related