javascriptnode.jsexpressmodels

create models in expressjs


I have an express app that gets its data from an external API

api.com/companies (GET, POST)
api.com/companies/id (GET, PUT)

I want to create a model to make the code easier to maintain as you can see I´m repeating a lot of code here.

router.get('/companies', function(req, res, next) {

    http.get({
        host: 'http://api.com',
        path: '/companies'
    }, function(response) {
        var body = '';
        response.on('data', function(d) {
            body += d;
        });
    });

    res.render('companies', {data: body});
});

router.get('/companies/:id', function(req, res, next) {

    http.get({
        host: 'http://api.com',
        path: '/companies/' + req.params.id
    }, function(response) {
        var body = '';
        response.on('data', function(d) {
            body += d;
        });
    });

    res.render('company', {data: body});
});

How can I do that?


Solution

  • First of all: http.get is asynchronous. Rule of thumb: When you see a callback, you are dealing with an asynchronous function. You can not tell, if the http.get() and its callback will be completed when you terminate the request with res.render. This means that res.render always needs to happen within the callback.

    I am using ES6 syntax in this example.

    // request (https://github.com/request/request) is a module worthwhile installing. 
    const request = require('request');
    // Note the ? after id - this is a conditional parameter
    router.get('/companies/:id?', (req, res, next) => {
    
        // Init some variables
        let url = ''; 
        let template = ''
    
        // Distinguish between the two types of requests we want to handle
        if(req.params.id) {
            url = 'http://api.com/companies/' + req.params.id;
            template = 'company';
         } else {
            url = 'http://api.com/companies';
            template = 'companies';
         }
    
        request.get(url, (err, response, body) => {
            
            // Terminate the request and pass the error on
            // it will be handled by express error hander then
            if(err) return next(err);
            // Maybe also check for response.statusCode === 200
    
            // Finally terminate the request
            res.render(template, {data: body})
        });
    
    });
    

    Regarding your 'model' question. I would rather call them 'services' as a model is some collection of data. A service is a collection of logic.

    To create a company service module could do this:

    // File companyService.js
    const request = require('request');
    
    // This is just one of many ways to encapsulate logic in JavaScript (e.g. classes)
    // Pass in a config that contains your service base URIs
    module.exports = function companyService(config) {
        return {
            getCompanies: (cb) => {
                request.get(config.endpoints.company.many, (err, response, body) => {
                    return cb(err, body);
                });
            },
            getCompany: (cb) => {
                request.get(config.endpoints.company.one, (err, response, body) => {
                    return cb(err, body);
                });
            },
        }
    };
    
    
    // Use this module like
    const config = require('./path/to/config');
    const companyService = require('./companyService')(config);
    
    // In a route
    companyService.getCompanies((err, body) => {
        if(err) return next(err);
    
        res.render(/*...*/)
    });