Ad

How To Break Logic Into A Controller And A Model In A Node

I do not quite understand how to properly break the logic on the controllers and models in nodeJS when working with the backend application. Suppose I have an example

This code is in the model of my application, and logically I understand that the model is only responsible for choosing from the database, and the controller and everything else should be done by the controller, but I don’t quite understand how to do this and I tried to transfer part of the code to the controller and export it, but I did not succeed (Please, help, at least with this example! The main thing for me is to understand the principle of working with MVC in the node !!!

exports.currentPostPage = function(req, res){


db.query('SELECT * FROM `posts`', function (err, result) {
    if (err){
      console.log(err);
    }
    var post = result.filter(item => {return (item.id == req.params.id)? item: false})[0];
    if (post === undefined){
      res.render('pages/404');
    } else {
      res.render('pages/post-page', {postId: req.params.id, item: post});
    }

  });
};
Ad

Answer

So, you're on the right track. There's a lot of different ways to do it depending on preferences, but one pattern I've seen pretty commonly is to use the callback as a way to integrate. For example, let's say you have your model file:

exports.getPostById = (id, cb) => {
  db.query('SELECT * FROM `posts` WHERE id=?', [id], function (err, result) {
    if (err){
      return cb(err); // or, alternatively, wrap this error in a custom error
    }
    // here, your logic is just returning whatever was returned
    return cb(null, result); 
  });
};

Note I also am letting the DB handling the ID lookup, as it's probably more efficient at doing so for larger data sets. You didn't say what DB module you're using, but all the good ones have some way of doing parametrized queries, so use whatever works w/ your DB driver.

Anyway, the Model file therefore handles just the data interaction, the controller then handles the web interaction:

// postController.js
const model = require('../models/postModel.js'); // or whatever you named it
exports.populatePost = (req, res, next, id) => {
  model.getPostById(id, (err, post) => {
    if (err) return next(err); // centralized error handler
    req.post = post;
    next(); 
  });
}

export.getOnePost = (req, res, next) => {
  if (req.post) {
    return res.render('pages/post-page', req.post);
  }
  // again, central error handling
  return next({ status: 404, message: 'Post not found' });
}

I have mentioned central error handling; I vastly prefer it to scattering error handling logic all over the place. So I either make custom errors to represent stuff, or just do like above where I attach the status and message to an anonymous object. Either will work for our purposes. Then, in a middleware file you can have one or more handler, the simplest like this:

// middleware/errors.js
module.exports = (err, req, res, next) => {
  console.error(err); // log it
  if (err.status) {
    return res.status(err.status).render(`errors/${err.status}`, err.message);
  } 
  return res.status(500).render('errors/500', err.message);
}

Finally, in your routing setup you can do things like this:

const postController = require('../controllers/postController');
const errorHandler = require('../middleware/errors.js');
const postRouter = express.Router();

postRouter.param('postId', postController.populatePost);
postRouter.get('/:postId', postController.getOnePost);
// other methods and routes

app.use('/posts', postRouter)

// later

app.use(errorHandler);

As was pointed out in the comments, some folks prefer using the Promise syntax to callbacks. I don't personally find them that much cleaner, unless you also use the async/await syntax. As an example, if your db library supports promises, you can change the model code to look like so:

exports.getPostById = async (id, cb) => {
  // again, this assumes db.query returns a Promise
  return await db.query('SELECT * FROM `posts` WHERE id=?', [id]);
}

Then your controller code would likewise need to change to handle that as well:

// postController.js
const model = require('../models/postModel.js'); // or whatever you named it
exports.populatePost = async (req, res, next, id) => {
  try {
    const post = await model.getPostById(id)
    req.post = post
    return next()
  } catch (err) {
    return next(err)
  }
}
Ad
source: stackoverflow.com
Ad