0

I'm writing my backend in Node/Express, and I'm looking for a way to create some OOP inheritance with my Express controllers.

My structure looks like:

.
└── server
    ├── App.js
    ├── controllers
    │   ├── articles.js
    │   └── project.js
    ├── models
    │   ├── articles.js
    │   ├── projects.js
    ├── routes
    │   ├── articles.js
    │   ├── projects.js
    └── views
        ├── error.ejs
        ├── index.ejs
        └── layout.ejs

There are some tasks common to some controllers that can't be handled by middlewares. For this, I want to create a basic controller class with some functions that will be shared by all controller instances.

Imagine that I have this routes:

// routes/project.js

router.put('/:id', upload.any(), putProject);    
router.post('/:id', upload.any(), postProject);

And the controllers:

// controllers/project.js

export const putProject = function(req, res, next) {
    var data = JSON.parse(req.body.data);
    // Retrieving files appended by Multer to req.files
    req.files.forEach((item) => {
        data[item.fieldname] = '/' + item.path;
    });

    models.Projects.update(data, {
        include: [{ model: models.ProjectImages }],
        returning: true,
        plain: true,
        where: {
            id: req.params.id,
        },
    });
    // etc...
};

export const postProject = function(req, res, next) {
    // Retrieving files appended by Multer to req.files
    var data = JSON.parse(req.body.data);
    req.files.forEach((item) => {
        data[item.fieldname] = '/' + item.path;
    });
    models.Projects.create({
        title: req.body.title,
        description: req.body.description,
        ArticleId: req.body.ArticleId,
    })
    // etc...
};

I would like both putProject and postProject extend a base clase that holds this logic:

var data = JSON.parse(req.body.data);
req.files.forEach((item) => {
    data[item.fieldname] = '/' + item.path;
});

If someone has an idea about how this can be written I would be grateful!

1 Answers1

4

The idiomatic way to do this with Express is to use middleware for the common bits and reuse that middleware in your routes. For example, this middleware can take care of parsing the project data:

function parseProject(req, res, next) {
  res.locals.project = JSON.parse(req.body.data);
  req.files.forEach((item) => {
    res.locals.project[item.fieldname] = '/' + item.path;
  })
  next();
}

...allowing each route handler to access res.locals.project to get that parsed content rather than repeating the code.

Then you can use your Router to more easily configure the common middlewares:

const projectRouter = express.Router();
projectRouter
  .use(upload.any())
  .use(parseProject)
  .put('/:id', putProject)
  .post('/:id', postProject);

app.use('/projects', projectRouter);

You could also just move that project parsing commonality to its own function and have the routes call the function.

Jacob
  • 77,566
  • 24
  • 149
  • 228
  • 2
    Just to add something that might be obvious for some, if the router also has methods like `.get()` and `.delete()` your middleware should handle the possibility of `req.body` being unset, or maybe just check the value of `req.method` – Aramil Rey Sep 14 '18 at 21:03
  • Thanks @Jacob, I like your approach, but as I said there are tasks that can't be handled in middlewares, see [multer req.body empty](https://stackoverflow.com/questions/39589022/node-js-multer-and-req-body-empty). So I still need to use the controllers. –  Sep 14 '18 at 21:26