3

I have created a API for different webpages with some CRUD functionality for POST, GET, etc. methods using Node.js, express and mongoose. I also have a big app.js file, where my routing logic and functions for CRUD methods reside.

So in the app.js file, I have to do the CRUD functionality and routing logic for every model in my models folder. This is quite much for on file, how can I separate the CRUD logic for my models, and the routing logic? So that it still works as normal without hosing my file?

I was thinking to separate the CRUD into a "controllers" folder and the routing into the "routes" folder, but I dont know how exactly, and what to require at what place..

My app.js looks like:

var express = require('express');
var app     = express();
var bodyParser = require("body-parser");
var morgan = require("morgan");
var routes = require('./routes');
var cors = require('cors')

//configure app
app.use(morgan('dev')); //log requests to the console

//configure body parser
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

var port = process.env.PORT || 5000;

//DATABASE SETUP
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/DNZ'); //connect to uor datbaase

//Handle the connection event, get reference to database.
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));

db.once('open', function() {
    console.log("DB connection alive");
});

//DNZ models live here
var FA = require('./models/DNZmodels/FA');
var FP = require('./models/DNZmodels/FP');
//ROUTES FOR OUR API
//=============================================================================

//create our router
var router = express.Router();

//middleware to use for all requests
router.use(function(req, res, next) {
    // do logging
    console.log('Something is happening.');
    console.log('Today is:', Date())
    next();
});

//test route to make sure everything is working (accessed at GET http://localhost:5000/DNZ/)
router.get('/', function(req, res) {
    res.json({ message: 'Welcome to DNZ API!' });   
});

//on routes that end in /FA
//----------------------------------------------------
router.route('/FA')

    // create a FA (accessed at POST http://localhost:8080/DNZ/FA)
    .post(function(req, res) {
        //console.log(req.body);
        //console.log(req.body.params);
        //res.setHeader('Content-Type', 'application/json')
        //res.send(JSON.stringify(req.body));
    /*
        var timestamp = req.body.Timestamp;
        var prognostizierterBetriebswert = req.body.PrognostizierterBetriebswert;
        var posFlexPot = req.body.posFlexPot;
        var negFlexPot = req.body.negFlexPot;
        var leistungsuntergrenze = req.body.Leistungsuntergrenze;
        var leistungsobergrenze = req.body.Leistungsobergrenze;
        var posGesEnergie = req.body.posGesEnergie;
        var negGesEnergie = req.body.negGesEnergie;
        var preissignal = req.body.Preissignal;
        var dummy1 = req.body.Dummy1;
        var dummy2 = req.body.Dummy2;
        var dummy3 = req.body.Dummy3;

        var fa = new FA({
            Timestamp: timestamp,
            Leistungsuntergrenze: leistungsuntergrenze,
            Leistungsobergrenze:leistungsobergrenze,
            PrognostizierterBetriebswert :prognostizierterBetriebswert,
            posFlexPot: posFlexPot,
            negFlexPot:negFlexPot,  
            posGesEnergie: posGesEnergie,
            negGesEnergie: negGesEnergie,
            Preissignal:preissignal,
            Dummy1: dummy1,
            Dummy2: dummy2,
            Dummy3: dummy3          
        })
        */

        //fa.name = req.body.name;
        console.log("Erzeugen der Instanz FA..");
        //console.log(Dummy1);
        //res.send(JSON.stringify(timestamp));

        // create a new instance of the FA model
        var fa = new FA(req.body);      

        //SAVE the new instance
        fa.save(function(err) {
            if (err) {
                console.log(err);
                res.status(400);
                res.send(err);
        }
        else {
            console.log("Instanz FA in Datenbank erzeugt!");
            res.status(200);
            res.json({ message: 'FA-Instance created in datbase!' });
        }
        });

    })

    // get all the FAs (accessed at GET http://localhost:8080/DNZ/FA)
    .get(function(req, res) {
        FA.find(function(err, fas) {
            if (err)
                res.send(err);

            res.json(fas);
        });
    });

//on routes that end in /FA/:FA_id
//----------------------------------------------------
router.route('/FA/:FA_id')

    // get the bear with that id
    .get(function(req, res) {
        FA.findById(req.params.bear_id, function(err, fa) {
            if (err)
                res.send(err);
            res.json(fa);
        });
    })

    /*
     * Athlete.
  find().
  where('sport').equals('Tennis').
  where('age').gt(17).lt(50).  //Additional where query
  limit(5).
  sort({ age: -1 }).
  select('name age').
  exec(callback);
     */
    // update the bear with this id
    .put(function(req, res) {
        FA.findById(req.params.FA_id, function(err, fa) {

            if (err)
                res.send(fa);

            //bear.name = req.body.name;
            /*
            FA.save(function(err) {
                if (err)
                    res.send(err);

                res.json({ message: 'FA updated!' });
            });
            */
        });
    });

    /*
    // delete the bear with this id
    .delete(function(req, res) {
        FA.remove({
            _id: req.params.bear_id
        }, function(err, FA) {
            if (err)
                res.send(err);

            res.json({ message: 'Successfully deleted' });
        });
    });
     */
//*************************************************************************
    //CREATE FP ROUTE
//*************************************************************************
router.route('/FP')

// create a FA (accessed at POST http://localhost:8080/DNZ/FP)
.post(function(req, res) {

    //res.setHeader('Content-Type', 'application/json') 
    console.log("Erzeugen der Instanz FP..");

    // create a new instance of the FP model
    var fp = new FP(req.body);      

    //SAVE the new instance
    fp.save(function(err) {
        if (err) {
            console.log(err);
            res.status(400);
            res.send(err);
    }
    else {
        console.log("Instanz FP in Datenbank erzeugt!");
        res.status(200);
        res.json({ message: 'FP-Instance created in datbase!' });
    }
    });

})

// get all the FAs (accessed at GET http://localhost:8080/DNZ/FA)
.get(function(req, res) {
    FP.find(function(err, fps) {
    if (err) {
        console.log(err);
        res.status(400);
        res.send(err);
    }
    else {
        //res.send("Willkommen auf /FP");
        res.json(fps);
    }
    });
});

//REGISTER OUR ROUTES -------------------------------and listen to requests
app.use('/DNZ', router);

//START THE SERVER
//=============================================================================



// set static directories
app.use(express.static('./dist'));
app.use(cors());

// Define Routes
var index = require('./routes/index');
var users = require('./routes/users');

//Set up routes
routes.init(app)

//run
app.listen(port);
console.log('Listen on port: ' + port);


console.log('Server started, Listening on port ',  port);
MMMM
  • 3,320
  • 8
  • 43
  • 80
  • 1
    take a look at this repository https://github.com/usman154/movieApp – Muhammad Usman May 16 '18 at 07:24
  • This is a very interesting question but it is very generic for SO. You should ask specific questions strictly related to specific problems you have with your code. For example, "I get error X, Y and Z with this...how can I fix it?". See this for [SO guidelines](https://stackoverflow.com/help/how-to-ask). – rags2riches-prog May 16 '18 at 07:24
  • @rags2riches I personally think this question is okay. Its not specifically regarding a problem, more a best practice. It is however only opinion based answering, but niché enough to be valid. IMHO, of course. – laminatefish May 16 '18 at 07:33

2 Answers2

0

This is primarily opinion based, but I had the same thing a while back and developed a way to extract route/model logic from the main file, using the module require-dir

In my app.js

/**
 * Process ALL routes from routes dir utilising require-dir
 *
 * Allows us to include an entire directory, without replicating
 * code, creating similar routes on a per end-point basis. This
 * nicely keeps all end-point routes separate.
 */
var routes = requireDir('./routes');
for(var x in routes) {
    application.use('/', routes[x]); // here, application is your express server instance
}

Then create a directory called routes and add whatever, for instance routes/branding.js:

var expressFramework = require('express');
var Branding = require('../models/branding');
var responseHelper = require('../shared/responseHelper');
var responseStatusCodes = require('../shared/responseStatusCodes');
var responseMessages = require('../shared/responseMessages');
var queryHelper = require('../shared/queryHelper');

var routerObject = expressFramework.Router();
var branding = new Branding();
var responsehelper = new responseHelper();
var queryhelper = new queryHelper();

/**
 * Endpoint /branding/{chain_id}/{site_id}
 */
routerObject.get('/branding/:chain_id/:site_id', function(req, res, next) {
    var requiredFields = [
        {
            chain_id: true, where: 'path'
        },
        {
            site_id: true, where: 'path'
        }
    ];

    if (!queryhelper.authenticateToken(req.headers.authorization)) {
        responsehelper.sendResponse(res, responseStatusCodes.STATUS_UNAUTHORISED,
            responseMessages.RESPONSE_AUTHENTICATION_FAILED);
        return;
    }

    if (!queryhelper.validateQuery(req, requiredFields)) {
        responsehelper.sendResponse(res, responseStatusCodes.STATUS_INVALID_QUERY_PARAMS,
            responseMessages.RESPONSE_INVALID_QUERY_PARAMS);
        return;
    }

    branding.getBranding(req, function(err, results, pagination) {
        if (err) return next(err);

        if (results.length >= 1) {
            responsehelper.sendResponse(res, responseStatusCodes.STATUS_OK, results);
        } else {
            responsehelper.sendResponse(res, responseStatusCodes.STATUS_NOT_FOUND,
                responseMessages.RESPONSE_NO_RECORDS_FOUND);
        }
    });
});

module.exports = routerObject;

The key point here, is that you eventually export the express Router object, which your application, can 'use'. You'll also notice, that branding uses an include var Branding = require('../models/branding'); - This contains all the logic, whereas the route contains the end point definitions only, a snippet:

var Promise         = require('bluebird');
var db              = require('../shared/db');
var queryHelper     = require('../shared/queryHelper');
var schemas         = require('../schemas/schemas');
var _               = require('lodash');
var serverSettings  = require('../shared/coreServerSettings');

// Create new instance of mysql module
var connection      = new db();
var queryhelper     = new queryHelper();

var queryAndWait    = Promise.promisify(connection.query);

// Branding Object
var Branding = function(data) {
    this.data = data;
};

Branding.prototype.data = {};

/**
 * Branding methods
 */

Branding.prototype.getBranding = function(req, callback) {
    var params = [];
    var queryFoundRows = `select FOUND_ROWS() as total`;
    var query = `select
        id
        from
        company
        where
        1=1
        and chain_id=?`;

    params.push(req.params.chain_id);


    queryAndWait(query + '; ' + queryFoundRows, params).then(function(result) {
        return Promise.map(result[0], function(row) {
            var params = [];
            var query = `select
                *
                from
                location
                where
                1=1
                and company_id=?
                and site_id=?`;

            params.push(row.id);
            params.push(req.params.site_id);

            return queryAndWait(query, params).then(function(result) {
                return result[0];
            });
        }).then(function(payload) {
            callback(null, payload);
        }).catch(function(err) {
            callback(err, null, null);
        });
    });
};

module.exports = Branding;

May not be exactly what you're after, but should provide a good start point. Good luck!

laminatefish
  • 5,197
  • 5
  • 38
  • 70
  • I should state that the code is quite old, and hasn't been touched for some time, so it's not ES6, or even that nice, but it works flawlessly. – laminatefish May 16 '18 at 07:31
0

This topic is subjective however - it is important to take a standard and stick to it. The way I handled this was by creating a subfolder with a module (using module.exports) and an init function that constructs the express app.

For every route I have another module that has an init function which accepts the express application as a parameter and then adds the routes in there.

Main code file:

var Api = require('./API/index.js');

File /API/Index.js:

var express = require('express');
/* Instantiations */
var app = express();
module.exports = {
...
apply(); 
...
}
var route1 = require('./Routes/route1.js');
var route2 = require('./Routes/route2.js');
/* all additional routes */

var Routes = [route1,route2,...]
function apply(){
    for(var i=0;i<Routes.length;i++){
        Routes[i].init(app);
    }
}

Then in API/Routes/route1.js and API/Routes/route2.js...

module.exports = {
    init: function(app){
        /* add your route logic here */
    }
}

The benefits of this approach in my experience is that you can optionally choose to add or remove routes as needed and it provides a readable route-path via the file system which is handy in most modern text editors.

Greg Rebisz
  • 156
  • 7