Want to get crystal clear about NodeJS app structure (Full JavaScript Stack)

12,431

Alright, this is a pretty broad question and I'm definitely no expert, but I'll do my best here.

TL;DR

  • routes are controllers that tell what logic to execute when a user navigates their browser to a certain path within your app, including which views to render and what data to send to those views
  • models are just that - data models within your application
  • module.exports = tells a file what exactly it "exports", that is what code needs to be executed or accessible from your main app file.
  • require(..) includes a module. You can set this on a variable so that you may call module functions later, or simply execute a function if that is all that module.exports returns.

Combining these techniques can help you nail down a solid framework for any of your applications.


Long Answer

Express provides a solid framework for structuring your Node.js application. Node is completely independent of Express, but because of how popular Express is they practically go hand-in-hand. Once installed, Express can be used to generate a scaffold web project (with options) for you to build on top of if you'd like.

Controllers

A generated project will create /routes/index.js, which (if you understand MVC) is essentially your main controller. A route in express is written as so:

app.get('/path', function(req, res, next){ .. } );

Lets break that down: our application variable (app) is being told that on a GET request to '/path' to execute an anonymous callback function with req, res, next variables (request, response, callback respectively). I find it helpful to think of this like a custom event handler.

Its important to note at this point that we could also call app.post with the same syntax for posts to a URL as opposed to gets.

Within our anonymous callback, we handle any incoming data and render a view for the user. This is where most of my business logic ends up, so it actually makes sense to NOT use anonymous functions here. Here's an example of a basic callback that just displays a homepage:

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

    //some business logic

    res.render('views/home');
});

When the user tries to GET the index path of our application (/), we simply render our home view that, from the root of our project, is stored in a views folder.

But what if we want to modularize this so that we aren't declaring all of our routes in our main app.js or server.js?

We use module.exports = .. in our modules to tell our server what exactly to include. In my controller, I export a single function that takes the application as an argument and uses that to define our routes like so:

Controllers/User.js

 module.exports = function(app){

    app.get('/users', function(req, res){
        var users = req.db.collection('users').find();
        if (!users) {
            console.log("no users found");
            res.redirect('/');
        } else {
            res.render('users/index', {users : users});
        }
    });

};

Don't worry about the req.db code, I attach the database to the request in my application but that isn't done by default. Simply understand that I'm getting a list of 'users' here, and redirecting the user to the index of my app if there aren't any.

Models

Mongoose provides us with a great interface for writing models. With mongoose, writing models is a three step process:

  • Define a schema
  • Define model logic
  • Generate and export the model

Here is an example of a User model:

Models/User.js

var mongoose = require('mongoose'),
    userSchema = new mongoose.Schema({

        name: { type: String, required: true },
        joinDate: {type: Date, default: date.now }

    }),
    User = mongoose.model('user', userSchema);

module.exports = user;

Server App

module.exports is used to help us define some modularity to our codebase. When we run a node application, we're ultimately running a single JavaScript file (you've already seen that file with server.js or app.js).

To keep this file from getting too big with multiple models and routes, we use require(module) to include code from other JS files. module in our case would be a path to the module we want to require. If you have the following doc structure:

| Controllers
    - User.js
| Models
    - User.js
| Views
app.js

To include your user controller from app.js, you would write: require('./Controllers/User'). Since our controller modules simply export functions, we can call that function immediately after our require statement by simply adding parentheses at the end (with whatever parameters are required). Including my controllers looks like so:

require('./Controllers/User')(app)

I'm passing in the actual app, because my module (below) simply exports a function that adds business logic to my app's routes. This only needs to be called and never used, so I don't capture my controller as a variable to call methods on later.

Including models is a little different, since we may want to perform some operation that our model defines. We can do this by changing up our require code just a bit:

var User = require('./Models/User');

Now we can call methods of our User model whenever. Mongoose gives us a lot of base functionality for free:

User.find({}, function(err, users){ .. });

The above function will go find all of our users, and then execute an anonymous function with a potential err (is null if no issues) and then a list of our users in JSON format. Pretty nifty.

Combining all of these concepts is how you create a basic web application using Express and Node.js. Please let me know in the comments if there's anything I can clarify about how I use Express. This is very surface level knowledge, and I suggest digging into documentation and looking at plugins to extend the capabilities of your apps. Good luck!

Share:
12,431
JustinHo
Author by

JustinHo

Updated on June 07, 2022

Comments

  • JustinHo
    JustinHo almost 2 years

    I would like to know the structure of a typical NodeJS app, because the more I read and see the projects, the more confused I am, specifically for questions like these (or even more after I updated this question):

    1. Take the MEAN stack for example, from what I know, NodeJS and Express take care of the server part, providing the server interface, etc. MongoDB and Angular are pretty straightforward.

      But where should the business logic go? Say if I have a controller.js which contains a function, and the route.js file binds the request with this controller function. My question is: under which module these files belong to/run under (Express or NodeJS?)

    2. Where is the starting point of a NodeJS app? Say index.php is the starting point of a PHP app, but where is it for NodeJS app? I can see that all Nodejs projects have a file called server.js or app.js, etc.(containing something like module.exports = app;) But how can NodeJS know which file to find and execute?

    I am a fresh noob on NodeJS, Express, sequelize.js/Mongoose, Jade/EJS but want to get started on a NodeJS project. Could you please elaborate on the actual function that each modules provide and a general introduction of the typical structure for a full JS stacked NodeJS app? Thanks in advance!