Building a web service with Node.js in Visual Studio Part 6: module.exports and controller basics

Introduction

In the previous post in this series we looked at the Node Package Manager and the basics in Express.js. We added a couple of endpoints to our web app and sent back some responses: HTML and JSON. We could in theory build our entire application on that but it’s of course not very wise to list all the endpoints and responses in server.js.

Instead we’d like to add routes and controllers just like in a “normal” MVC app. We can certainly do that in a Node.js project. We’ll add the routes and controllers in their separate JS files to keep the project modular.

There are no conventions available here. If you call /customers then the call won’t magically be routed to the customers controller. There’s no object called “controller” that you can inherit from to mark your JS object as controller. We’ll need to declare all of that ourselves. Therefore you can name your controllers as you wish such as “customersController” or “mickeyMouse”, it won’t make any difference from a programmatic point of view. However, we’ll follow the commonly accepted MVC naming rules so that we know which JS file does what.

module.exports

Before we continue though there’s something else you need to be familiar with in Node: the “module” and the “exports”.

We saw the “require” method before but we haven’t discussed what it returns. We just assigned its return value to a variable, like…

var http = require('http');
var express = require('express');

The require method will return an object that represents the publicly available elements of the module behind the library referenced by the string input variable. Within the module there can be one or more public fields and methods that are exported so that callers of the module will have access to them. You expose the public functions in a module using the “module.exports” statement. So “module.exports” is sort of a bucket where you can add the public methods and properties of a module. Let’s see an example.

Add a new folder called “modules” to the project and add a new JS file called “module_example.js”. Let’s add a public function to it:

module.exports.greatFunction = function()
{
    return "Greetings from great function!";
};

We’re stating that the module exposes – or exports – a function called greatFunction which returns a string.

You can call this function from server.js as follows:

var exampleModule = require('./modules/module_example.js');
var ret = exampleModule.greatFunction();

Note that we referenced “module_example” by a relative file path as opposed to modules installed via the npm which only required a simple name. Once we have a reference to the module in “module_example.js” we can call its exposed “greatFunction” function.

We can assign a default function to our module. Add the following code to module_example.js:

module.exports = function()
{
    return "Greetings from a single method module!";
};

Note that we didn’t give any name to the function. We can call this as follows from server.js:

var ret = exampleModule();

So we simply call exampleModule as if it is a method.

We can also have properties in a module:

module.exports.defaultGreeting = "Hello from property";

…and here’s how you can expose a constructor by modifying the default function above:

module.exports = function () {
    this.greeting = "Hello from constructor!";
};

…and call it from server.js as follows:

var ex = new exampleModule();
var greeting = ex.greeting;

Your own modules can also have their own “require” statements to reference their own dependencies and those dependencies can reference other dependencies as well. Keep in mind that server.js is the entry point of the application, like Global.asax in a .NET web app. It is run once upon application start and executes the dependency code it finds in form of those require statements. This execution only occurs once upon application start-up and then all results from the require statements are cached.

Controllers

Add a new folder called “controllers” to the project. Right-click it, select Add… New Item… and add a new JavaScript file called customersController.js. As mentioned in the introduction there’s no special programmatic reason to call a controller a “controller” in Node.js but it’s good to follow the MVC conventions anyway to make navigating the project modules easier.

We’ll expose – or think of this as “export” to make the naming of module.exports easier – our get, post etc. methods for the customersController using module.exports as we’ve seen above. We’ve seen an example of declaring the GET method function for our application in server.js:

var app = express();

app.get("/customers", function (req, res) {
    res.set('Content-Type', 'application/json');
    res.send({name: "Great Customer", orders: "none yet"});
});

The goal is to move that into the controller. The initial code in customersController will be as follows:

module.exports.start = function (app) {
    app.get("/customers", function (req, res) {
        res.set('Content-Type', 'application/json');
        res.send({ name: "Great Customer", orders: "none yet" });
    });
};

We expose a “start” method which accepts the app object and assign the GET method within the method body. You’ll recognise the GET method implementation itself.

Back in server.js we’ll reference the entire “controllers” folder using the require method. Add the following code to server.js just below the require(‘express’) statement:

var controllers = require('./controllers');

This statement will automatically reference and execute all elements within the controllers folder so we don’t need to reference them one by one. For this to work though there will need to be a file called index.js in that folder. We’ll use index.js to initialise all controllers so it works like a routing engine. Add a new file called index.js to the “controllers” folder and add the following content to the file:

var customerController = require('./customersController');

module.exports.start = function (app) {
    customerController.start(app);
};

Index.js will need to be extended as you add new controllers and call their start methods in turn. This way we can have one central file to take care of the initialisation of all controllers. Server.js will only need to know about “controllers” in general through an indirect reference to index.js in the require(‘./controllers’) statement. Server.js will be able to call “start” on the controllers object which will call the start method in the index.js file. Which in turn will initialise the controllers.

Back in server.js we can insert the following code after app = server():

controllers.start(app);

Just to recap we have the following code in server.js where I’ve omitted the example code of the “module.exports” section:

var http = require('http');
var express = require('express');
var controllers = require('./controllers');

var app = express();

controllers.start(app);

var port = process.env.port || 1337;
http.createServer(app).listen(port);

Run the application, navigate to /customers and you should see the same JSON response as before. However, now we have the skeleton of a modularised Node.js project with controllers and a central file for initialising the controllers.

How do we add query parameters to the route? Open customersController again and add a new route within the body of the start function:

app.get("/customers/:id", function (req, res) {
        var customerId = req.params.id;

        res.set('Content-Type', 'application/json');
        res.send({ name: "Great Customer with id " + customerId, orders: "none yet" });
    });

We attach the place of the query parameter with a colon and read it using req.params, easy as that.

Run the application, navigate to /customers/123 and you should see the following JSON on the screen:

{"name":"Great Customer with id 123","orders":"none yet"}

In the next post we’ll discuss asynchronous code execution a bit more and we’ll see how to organise our code into services and repositories.

View all posts related to Node here.

Advertisements

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

ultimatemindsettoday

A great WordPress.com site

iReadable { }

.NET Tips & Tricks

Robin Sedlaczek's Blog

Developer on Microsoft Technologies

HarsH ReaLiTy

A Good Blog is Hard to Find

Ricos Blog zu Softwaredesign- und architektur

Ideen und Gedanken rund um Softwaredesign und -architektur, Domain-Driven Design, C# und Windows Azure

the software architecture

thoughts, ideas, diagrams,enterprise code, design pattern , solution designs

Technology Talks

on Microsoft technologies, Web, Android and others

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

Anything around ASP.NET MVC,WEB API, WCF, Entity Framework & AngularJS

Cyber Matters

Bite-size insight on Cyber Security for the not too technical.

Guru N Guns's

OneSolution To dOTnET.

Johnny Zraiby

Measuring programming progress by lines of code is like measuring aircraft building progress by weight.

%d bloggers like this: