Search code examples
javascriptnode.jsmongodbexpressdirectory-structure

How can I abstract code out of server.js into subdirectories in my Express app?


I'm new to Node and I am trying to build out an API using Express and MongoDB for a web app I'm building. I'm following some tutorials and I have some basic routes working, but right now all of my code lives in a single file, server.js. I'd like to move some of it out into sub directories to organize the code a bit better. How can I do that?

var express = require('express'),
    mongoose = require('mongoose'),
    app = express();

var userSchema = new mongoose.Schema({
    email: {
        type: String
    },
    password: {
        type: String
    }
});

var UserModel = mongoose.model('user', userSchema);

app.use(function(req, res, next) {
    res.setHeader('Access-Control-Allow-Origin', 'http://localhost:4200');
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    res.header('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, OPTIONS');
    next();
});

mongoose.connect('mongodb://localhost/mymongocollection');

app.get('/user', function(req,res) {
    // do something
});

app.post('/user', function(req,res) {
    // do something
});

app.listen('4500');

This what I think makes sense:

- server.js
- routes/
  - user.js
- models/
  - user.js

Inside server.js, it looks like I'll be using require()?

var userModel = require('models/user.js);

I've read about module in Node and module.exports but I haven't really wrapped my head around it yet. How does that work?

Lastly, should the user schema go in the models/user.js file? Or should schemas live in their own directory?


Solution

  • There really is no right or wrong way to structure a node application, as long as it works. It really comes down to personal preference for the most part. With that being said there are some conventions that are used quite often to help keep things organized and sane.

    The two main ways you structure an application would be to separate files based on module type (routes, models, etc) or based on feature (user, products, etc)

    Example Method 1

    + app
      + routes
        - user.js
        - product.js
      + models
        - user.js
        - product.js
    - config.js
    - server.js
    

    Example Method 2

    + app
      + user
        - model.js
        - route.js
      + product
        - model.js
        - route.js
    - config.js
    - server.js
    

    Personally my setup usually looks something like:

    + app
      + controllers
        - user.js
        - product.js
      + models
        - user.js
        - product.js
      + templates
        - user.html
        - product.html
      + utils
        - some_cool_thing.js
      - config.js
      - router.js
      - server.js
    

    I have one large router.js that contains routes for the entire application instead breaking out routes for each feature. All the logic for each route is contained in a file in the controllers directory. By no means is this typical / perfect / or ideal. It's just my personal preference.

    Now in terms of actual files, what you have is really close, but you should try to avoid passing the app or mongoose objects to your other modules. Each module should be isolated from the rest of the application as much as possible. If your model needs mongoose simply require the mongoose module instead of passing it in. In the routes, you would make use of the express router, and then export the router from the route module, then you would require and use the route module in your application.

    Example:

    server.js

    var express = require('express'),
        mongoose = require('mongoose'),
        router = require('./router'), // this is your router.js file
        app = express();
    
    // connect
    mongoose.connect('mongodb://localhost/mymongocollection');
    
    // routes
    app.use('/', router); // use your routes on the root of the application
    // app.use('/api', router); // you could easily use your routes prefixed with `/api`
    
    // listener
    app.listen('4500');
    

    router.js

    var router = require('express').Router(); // https://expressjs.com/en/4x/api.html#router
    
    // controllers
    var Users = require('./controllers/user.js');
    var Products = require('./controllers/products.js');
    
    // routes
    router.route('/user')
    .get(Users.findAll)
    .post(Users.create);
    
    router.route('/users/:id')
    .get(Users.findOne)
    .put(Users.update)
    .delete(Users.remove);
    
    ...
    

    controllers/user.js

    var UserModel = require('../models/user.js');
    
    exports.findAll = function(req, res, next) {
        UserModel.find(function(err, users) {
            if (err) return next(err);
    
            res.send(users);
        }
    }
    
    exports.create = function(req, res, next) {
        var user = new UserModel();  
    
        user.name = req.body.name;
    
        user.save(function(err) {
            if (err) return next(err);
    
            res.send('user created');
        });
    } 
    
    ...
    

    models/user.js

    var mongoose     = require('mongoose');
    var Schema       = mongoose.Schema;
    
    var UserSchema   = new Schema({
        name: String
    });
    
    module.exports = mongoose.model('User', UserSchema);
    

    Note: I'm not overly familiar with Mongo / Mongoose as it's been quite some time since I've used it either of them on a project, so the syntax may be a bit off. The above examples are meant more for illustrative purposes, they have not been tested or debugged.