This is the third section of the sixth part of Building a web application using ASP.NET Core 1.0, Node.Js, Aurelia, TypeScript, Webpack in Visual Studio Code.
The source code for this tutorial is available on GitHub.
In this section of part 6, I’ll show how to enable CORS (Cross Origin Resource Sharing) on the ExpressApplication and HapiApplication. Then, I’ll show how to include both Asp.Net Core and NodeJs backend REST API implementations to work with a single copy of front end Aurelia client code in a single folder structure. Finally, I’ll show you how you can use either Asp.Net Core or NodeJs as your REST API endpoints by changing a configuration value in the front end code.
Enabling CORS in ExpressApplication and HapiApplication
Since our client application’s port and our server side REST API’s port will be different, we need to enable CORS to support cross-origin requests. We have already done this in our RestifyApplication class but we need to enable CORS in both ExpressApplication and HapiApplication.
To allow CORS, we need to add the headers “Access-Control-Allow-Origin”, “Access-Control-Allow-Headers” and “Access-Control-Allow-Methods” to the allows origins like “http://localhost:9000”, allowed headers like “Origin, Accept, Authorization” and allowed HTTP methods/verbs like “GET, POST, PUT, DELETE” etc. For demonstration purposes, we can use “*” as the value to allow any origin, any header or any method, which is not recommended for production deployment. When I talk about production deployment in later part of the series, I’ll show how to restrict them to only the absolutely required origins, headers and HTTP Verbs.
Instead of manually setting these headers and their values, let us leverage the cors module for expressjs. To install the cors module for expressjs and its typescript typing definition, run the commands:
npm install cors --save
typings install dt~cors --save --global
We need to import the cors module like this:
import * as cors from "cors";
Let us refactor the constructor of ExpressApplication and add CORS support into the bootstrap method like this:
express-application.ts
this.expressApplication.use(cors(), (request: express.Request, response: express.Response, next: express.NextFunction) => {
console.log("CORS enabled for all routes");
next();
});
// configure API and error routes
this.expressContactRouter.configApiRoutes(this.expressApplication);
this.expressContactRouter.configErrorRoutes(this.expressApplication);
this.expressApplication.set("port", port);
...
}
Let us refactor the constructor of HapiApplication and add CORS support into the bootstrap method like this:
Let us refactor the constructor of RestifyApplication and move CORS support into the bootstrap method like this:
restify-application.ts
public bootstrap(port: number) {
this.restifyApplication.use(restify.CORS());
this.restifyApplication.use(restify.pre.sanitizePath());
this.restifyApplication.use(restify.acceptParser(this.restifyApplication.acceptable));
this.restifyApplication.use(restify.bodyParser());
this.restifyApplication.use(restify.queryParser());
this.restifyApplication.use(restify.authorizationParser());
this.restifyApplication.use(restify.fullResponse());
// configure API and error routes
this.restifyContactRouter.configApiRoutes(this.restifyApplication);
this.restifyContactRouter.configErrorRoutes(this.restifyApplication);
...
}
Notice that I removed the line restify.CORS.ALLOW_HEADERS.push("authorization"); because we don’t need to add this header explicitly. We’ll revisit this header when I talk about authentication and authorization in a later part of this series.
Generating UML class diagrams from TypeScript modules
You can generate UML diagram from TypeScript modules using a simple tool named tsviz. The github repository for this tool is https://github.com/joaompneves/tsviz. To install the tool use the command:
npm install tsviz -g
To run the tool and create a diagram for an entire project, you use the command:
Modify the route for GET method to take an optional id parameter
Since the Aurelia client application will need to pass an optional id parameter to the GET endpoint, let us add that optional parameter to all three router classes.
Let us modify the configApiRoutes() method in ExpressContactRouter like this:
Merging source code from part5 into part6 to have both Asp.Net Core and NodeJs backend APIs with a single Aurelia front end source code
We are interested to keep our ASP.NET Core backend and the Node.js backend server code in the same project structure with the Aurelia client code shared between them. We need to first bring the source code from part5 into part6 using the following steps:
1. Create a client folder under part6\modern folder
2. Copy part5/modern/src folder into part6/modern/client folder
3. Copy all files and folders in part5/modern except the src folder into part6/modern folder
Running both Asp.Net Core 1.0 and NodeJs REST Service from 2 instances of VS Code
If we want to run multiple task runners from within a single instance of VS Code, we could do so using tasks.json and assigning custom key combinations to launch TypeScript compiler, DotNet compiler, npm, gulp etc. VS Code has launch.json to launch one type of application with “request” types like “launch” and “attach”. At the time of this writing, VS Code doesn’t allow us to define mulitple launch configurations say one for .NET Core and another for NodeJs. You can have one or another with both “request” types like “launch” and “attach” but not both project types with both “request” types.
The best way that I know of to run both Asp.Net Core and NodeJs applications within a single folder structure is to put them in different sub folders and launch 2 instances of VS Code.VS Code treats the folder from where you launch VS Code as the project folder. Technically, any folder where you have a .vscode folder is a project because .vscode folder is where you define tasks.json, launch.json and settings.json. Since we can’t and shouldn’t duplicate the client source code, we have already created a symlink inside of NodeJs folder pointing to the client source code within Asp.Net project folder. So, we don’t have to worry about maintaining 2 copies of the client code because synchronizing them would become a nightmare.
We should open up an instance of VS Code from part6/modern to debug the Asp.Net Core REST API as well as launching the Aurelia client web application. We should open up an instance of VS Code from part6/modern/nodejs to debug the NodeJs REST API.
Configuring the client code to use Asp.Net Core 1.0 or NodeJs REST Service endpoint
To configure the client code to user Asp.Net Core 1.0 or NodeJs REST Service endpoint, we’ll use the “baseApiUrl” variable under client/src/common/config.ts file. To use Asp.Net Core 1.0 REST Service endpoint, use this:
public static baseApiUrl = "http://localhost:5000/api/";
To use Asp.Net Core 1.0 REST Service endpoint, use this:
Changing the implementation of Delete endpoint in Asp.Net core to take a JSON object
In my first implementation, I was sending the id of the contact to be deleted in the HTTP body to the Delete endpoint. Let us change it to a JSON object like this:
{ id: 1 }
Let us define a model class named DeletePayload under Models folder like this:
namespace Modern.Models
{
public class DeletePayload
{
public int id { get; set; }
}
}
Change the signature and implementation of Delete() method in ContactsController to take a DeletePayload object like this:
[HttpDelete]
public IActionResult Delete([FromBody] DeletePayload payload)
{
_contactsRepository.Delete(payload.id);
return new NoContentResult();
}
Changing the implementation of delete method in ContactService class
Let us change the implementation of delete method in ContactService under client/src/services/ to send the deletePayload json object like this:
Running the Aurelia client app with Asp.Net Core and NodeJs
Since we have launched 2 instances of VS Code, you can run the REST application using F5 key. Now, both instances of VS Code will be waiting to serve the requests coming from the client application. First, you can change the port number of baseApiUrl variable in config.ts to 8080 to use NodeJs. You can launch the client application from the browser using the url http://localhost:9000 . You can test different endpoints from the UI by selecting a contact, removing a contact, modifying a contact and by adding a new contact. After you have tested the NodeJs REST API endpoints, you can switch the port number of baseApiUrl variable in config.ts to 5000 to use Asp.Net Core 1.0. You’ll repeat the same set of operations from the UI to test the Asp.Net Core 1.0 REST API endpoints.
Conclusion
In section 3 of part 6, we have seen how to bring both Asp.Net Core 1.0 and NodeJs backend REST Service implementation into a single folder structure and leveraging the same Aurelia client application source code. Now, we can debug Asp.Net Core 1.0 and NodeJs backend REST Service implementation from 2 instances of VS Code but leveraging the same client side code. We are going to use both Asp.Net Core 1.0 and NodeJs backends in the future parts. This section concludes part 6. Happy Coding!
This is the second section of the sixth part of Building a web application using ASP.NET Core 1.0, Node.Js, Aurelia, TypeScript, Webpack in Visual Studio Code.
The source code for this tutorial is available on GitHub.
In this part, I’m going to show how to extract the routes from the Application class into its own router class. Then, I’ll show to use Aurelia’s dependency injection container to inject the dependencies so we don’t have to create the objects explicitly using new operator.
Important Changes
There are a couple of important changes since I posted the last section of part 6 of this series. Node.Js v6.9.0 LTS is now available for download at https://nodejs.org/en/. VS Code 1.6.1 is now available for downloat ad https://code.visualstudio.com/download . Aurelia now has a VS Code plugin, which support syntax highlighting and code completion. Check out the VS Code Aurelia plugin announcement page at http://blog.aurelia.io/2016/10/11/introducing-the-aurelia-vs-code-plugin/ To install the plugin, press CTRL + P from within VS Code and use the following command:
ext install aurelia
After you hit Enter key, you’ll see the aurelia extensions in the “EXTENSIONS” pane and select “aurelia 0.1.5” and click “Install” button. After the installation is completed, click “Enable” button to enable the VS Code Aurelia plugin. VS Code will ask you to restart VS Code and select “OK”.
Please make sure to install Node.Js v6.9.0 LTS, VS Code 1.6.1 and VS Code Aurelia plugin.
Refactoring the routes in all Application classes into their own classes
The application class for each of those 3 frameworks is also defining the routes and the call back functions. It makes sense to refactor the routes into their own classes. Let us create a folder named routes and create routes classes for Express, Hapi and Restify frameworks. While we do this refactoring, let us also add the methods to handle POST, PUT and DELETE HTTP verbs to fully expose the CRUD operation as APIs.
Creating a router class for Express
After wiring the body parser in the pipeline, let us add the post() method that can receive a contact in http body as json that can be persisted on the back end. Let us also add the put() and delete() methods that can modify a contact and delete a contact respectively.
Let us create a class named ExpressContactRouter and move the routing methods configApiRoutes() and configErrorRoutes() method from ExpressApplication into ExpressContactRouter like this:
We can now remove the unwanted import statements, unwanted methods configApiRoutes(), configApiRoutes(), add the import statement for ExpressContactRouter , remove the route objects and add the ExpressContactRouter property like this:
"use strict";
import * as express from "express";
import * as debug from "debug";
import * as http from "http";
import {INodeJsApplication} from "./inodejs-application";
import {ExpressContactRouter} from "../routes/express-contact-router";
export class ExpressApplication implements INodeJsApplication {
private expressApplication: express.Application;
private expressContactRouter: ExpressContactRouter;
Let us change the constructor of ExpressContactRouter to invoke the routing methods configApiRoutes() and configErrorRoutes() in ExpressContactRouter like this:
constructor() {
// create expressjs application
this.expressApplication = express();
// create express contact routes
this.expressContactRouter = new ExpressContactRouter();
// configure API and error routes
this.expressContactRouter.configApiRoutes(this.expressApplication);
this.expressContactRouter.configErrorRoutes(this.expressApplication);
}
Creating a router class for Hapi
Let us add the post() method that can receive a contact in http body as json that can be persisted on the back end. Let us also add the put() and delete() methods that can modify a contact and delete a contact respectively.
Let us create a class named HapiContactRouter and move the routing methods configApiRoutes() and configErrorRoutes() method from HapiApplication into HapiContactRouter like this:
We can now remove the unwanted import statements, unwanted methods configApiRoutes(), configApiRoutes(), add the import statement for HapiContactRouter , remove the route objects and add the HapiContactRouter property like this:
"use strict";
import * as Hapi from "hapi";
import * as Boom from "Boom";
import {INodeJsApplication} from "./inodejs-application";
import {HapiContactRouter} from "../routes/hapi-contact-router";
export class HapiApplication implements INodeJsApplication {
public hapiApplication: Hapi.Server;
private hapiContactRouter: HapiContactRouter;
Let us change the constructor of HapiContactRouter to invoke the routing methods configApiRoutes() and configErrorRoutes() in HapiContactRouter like this:
constructor() {
console.log("HapiApplication ctor");
// create expressjs application
this.hapiApplication = new Hapi.Server();
this.hapiContactRouter = new HapiContactRouter();
}
Creating a router class for Restify
Let us add the post() method that can receive a contact in http body as json that can be persisted on the back end. Let us also add the put() and delete() methods that can modify a contact and delete a contact respectively.
Let us create a class named RestifyContactRouter and move the routing methods configApiRoutes() and configErrorRoutes() method from RestifyApplication into RestifyContactRouter like this:
We can now remove the unwanted import statements, unwanted methods configApiRoutes(), configApiRoutes(), add the import statement for RestifyContactRoutes , remove the route objects and add the RestifyContactRouter property like this:
"use strict";
import * as restify from "restify";
import {INodeJsApplication} from "./inodejs-application";
import {RestifyContactRouter} from "../routes/restify-contact-router";
export class RestifyApplication implements INodeJsApplication {
private restifyApplication: restify.Server;
private restifyContactRouter: RestifyContactRouter;
Let us change the constructor of RestifyContactRouter to invoke the routing methods configApiRoutes() and configErrorRoutes() in RestifyContactRouter like this:
constructor() {
console.log("RestifyApplication ctor");
// create restify server
this.restifyApplication = restify.createServer();
restify.CORS.ALLOW_HEADERS.push("authorization");
this.restifyApplication.use(restify.CORS());
this.restifyApplication.use(restify.pre.sanitizePath());
this.restifyApplication.use(restify.acceptParser(this.restifyApplication.acceptable));
this.restifyApplication.use(restify.bodyParser());
this.restifyApplication.use(restify.queryParser());
this.restifyApplication.use(restify.authorizationParser());
this.restifyApplication.use(restify.fullResponse());
// configure contact routes
this.restifyContactRouter = new RestifyContactRouter();
// configure API and error routes
this.restifyContactRouter.configApiRoutes(this.restifyApplication);
this.restifyContactRouter.configErrorRoutes(this.restifyApplication);
}
Using DI in Node.js by leveraging Aurelia DI Container module
Let us leverage Aurelia’s DI Container in Node.js because the Constructor Injection pattern used in Aurelia is very neat. This would also help us validate that a well designed non-UI or DOM specific client side JavaScript libraries can easily be reused on the JavaScript Server Side platform like Node.js. We don’t need the Aurelia framework to use Aurelia dependency injection module because there is no dependency on the framework from DI container module. Let us install aurelia dependency injection and aurelia polyfills modules using the command:
Under the di folder, create a file named aurelia-di-container.ts and add this code:
import "aurelia-polyfills";
import {Container} from "aurelia-dependency-injection";
import {NodeServer} from "../../node-server";
export class AureliaDIContainer {
static bootstrap() {
let container = new Container();
let nodeServer = container.get(NodeServer);
nodeServer.bootstrap();
}
}
AureliaDIContainer.bootstrap();
In lines 6-9, we define a static method named bootstrap(), construct an instance of Aurelia’s Container class, then get an instance of NodeServer class and invoke its bootstrap method. In line 13, we invoke that static bootstrap(0 of AureliaDIContainter class.
After defining AureliaDIContainer, we need to change the “program” setting in launch.json to point to aurelia-di-containter.ts like this:
Refactoring NodeJsFrameworkFactory using Aurelia DI
Instead of creating the NodeJs Application classes using new operator, let us inject the instances into NodeJsFrameworkFactory using aurelia’s @inject decorator. When we use @inject decorator, we need to make sure that we have the constructor signature match the list of dependencies in the inject decorator. Decorators is an experimental feature in ECMAScript 7 and in TypeScript. So, we need to enable that feature in tsconfig.json using this setting:
“experimentalDecorators”: true
nodejs-framework-factory.ts
import {inject} from "aurelia-dependency-injection";
import {ExpressApplication} from "./express-application";
import {RestifyApplication} from "./restify-application";
import {HapiApplication} from "./hapi-application";
import * as _ from "lodash";
@inject(ExpressApplication, RestifyApplication, HapiApplication)
export class NodeJsFrameworkFactory {
private frameworkInstances: any;
constructor(private expressApplication: ExpressApplication, private restifyApplication: RestifyApplication,
private hapiApplication: HapiApplication) {
console.log("NodeJsFramework ctor");
this.frameworkInstances = {};
for(let argument of arguments) {
let typeName:string = _.startCase(argument.constructor.name);
this.frameworkInstances[`${typeName} Application`] = argument;
}
}
public createNodeJsFramework(restFramework: string) : any {
return (restFramework) ? this.frameworkInstances[_.startCase(`${restFramework} Application`)] :
this.frameworkInstances["Express Application"];
}
}
In line 7, we inject the dependencies ExpressApplication, RestifyApplication and HapiApplication using inject decorator. In line 10, we define the properties that match the list of dependencies in the inject decorator. From line 13 to 16, we create an array to hold the instances of the Application classes pertaining to the three NodeJs frameworks. We enumerate the function’s arguments collection, which in this case would be three that matches the dependencies in the inject decorator. We get the name of the class using argument.constructor.name to use as the key into the frameworkInstances object.
Refactoring NodeServer to inject NodeJsFrameworkFactory
Let us refactor NodeServer class to inject NodeJsFrameworkFactory and remove the code that instantiates nodeServer and invoke nodeServer.bootstrap() since that is now done in AureliaDIContainer class.
node-server.ts
import {inject} from "aurelia-dependency-injection";
import {NodeJsFrameworkFactory} from "./contact-manager/bootstrap/nodejs-framework-factory";
@inject(NodeJsFrameworkFactory)
export class NodeServer {
constructor(private nodejsFrameworkFactory : NodeJsFrameworkFactory) {
console.log("NodeServer ctor");
}
Refactoring ExpressApplication, RestifyApplication and HapiApplication classes to inject their Router classes
Instead of creating an instance of ExpressContactRouter using new operator, we will inject an instance of ExpressContactRouter into the constructor of ExpressApplication using inject decorator like this:
express-application.ts
import {inject} from "aurelia-dependency-injection";
@inject(ExpressContactRouter)
export class ExpressApplication implements INodeJsApplication {
private expressApplication: express.Application;
constructor(private expressContactRouter: ExpressContactRouter) {
// create expressjs application
this.expressApplication = express();
// configure API and error routes
this.expressContactRouter.configApiRoutes(this.expressApplication);
this.expressContactRouter.configErrorRoutes(this.expressApplication);
}
Instead of creating an instance of HapiContactRouter using new operator, we will inject an instance of HapiContactRouter into the constructor of HapiApplication using inject decorator like this:
hapi-application.ts
import {inject} from "aurelia-dependency-injection";
@inject(HapiContactRouter)
export class HapiApplication implements INodeJsApplication {
public hapiApplication: Hapi.Server;
constructor(private hapiContactRouter: HapiContactRouter) {
console.log("HapiApplication ctor");
// create expressjs application
this.hapiApplication = new Hapi.Server();
}
Instead of creating an instance of RestifyContactRouter using new(), we will inject an instance of RestifyContactRouter into the constructor of RestifyApplication using inject decorator like this:
restify-application.ts
import {inject} from "aurelia-dependency-injection";
@inject(RestifyContactRouter)
export class RestifyApplication implements INodeJsApplication {
private restifyApplication: restify.Server;
constructor(private restifyContactRouter: RestifyContactRouter) {
console.log("RestifyApplication ctor");
// create restify server
this.restifyApplication = restify.createServer();
restify.CORS.ALLOW_HEADERS.push("authorization");
this.restifyApplication.use(restify.CORS());
this.restifyApplication.use(restify.pre.sanitizePath());
this.restifyApplication.use(restify.acceptParser(this.restifyApplication.acceptable));
this.restifyApplication.use(restify.bodyParser());
this.restifyApplication.use(restify.queryParser());
this.restifyApplication.use(restify.authorizationParser());
this.restifyApplication.use(restify.fullResponse());
// configure API and error routes
this.restifyContactRouter.configApiRoutes(this.restifyApplication);
this.restifyContactRouter.configErrorRoutes(this.restifyApplication);
}
Refactoring ExpressContactRouter, RestifyContactRouter and HapiCotactRouter classes to inject InMemoryContactService
Instead of creating an instance of InMemoryContactService using new operator, we will inject an instance of InMemoryContactService into the constructor of ExpressContactRouter using inject decorator like this:
express-contact-router.ts
import {inject} from "aurelia-dependency-injection";
@inject(InMemoryContactService)
export class ExpressContactRouter {
private contactRouter: express.Router;
constructor(private contactService: InMemoryContactService) {
this.contactRouter = express.Router() }
Instead of creating an instance of InMemoryContactService using new operator, we will inject an instance of InMemoryContactService into the constructor of HapiContactRouter using inject decorator like this:
hapi-contact-router.ts
import {inject} from "aurelia-dependency-injection";
@inject(InMemoryContactService)
export class HapiContactRouter {
constructor(private contactService: InMemoryContactService) {
console.log("HapiApplication ctor");
}
Instead of creating an instance of InMemoryContactService using new operator, we will inject an instance of InMemoryContactService into the constructor of RestifyContactRouter using inject decorator like this:
restify-contact-router.ts
import {inject} from "aurelia-dependency-injection";
@inject(InMemoryContactService)
export class RestifyContactRouter {
constructor(private contactService: InMemoryContactService) {
console.log("RestifyContactRoutes ctor");
}
You can run the application using all 3 frameworks now using the REST_FRAMEWORK environment variable in launch.json. To test Express, Hapi and Restify frameworks, we need to just change the REST_FRAMEWORK to “express” or "hapi" or "restify" in launch.json
Using Aurelia DI Resolvers
If we don’t want to create the instances eagerly, which is the default resolver used by Aurelia internally, we can explicitly specify which resolver to use to provide the instances.The resolver that we could use is Lazy, which injects a function for lazily evaluating the dependency. The DI resolvers are part of aurelia framework, which we should install using the command:
npm install aurelia-framework --save
Instead of injecting the actual instance, we’ll inject the resolver Lazy.of(type), which will inject a function that returns the type that can be used later. Let us just modify NodeJsFrameworkFactory to use Lazy.of() resolver like this:
nodejs-framework-factory.ts
import {inject} from "aurelia-dependency-injection";
import {Lazy} from "aurelia-framework";
import {ExpressApplication} from "./express-application";
import {RestifyApplication} from "./restify-application";
import {HapiApplication} from "./hapi-application";
import * as _ from "lodash";
@inject(Lazy.of(ExpressApplication), Lazy.of(RestifyApplication), Lazy.of(HapiApplication))
export class NodeJsFrameworkFactory {
private frameworkInstances: any;
constructor(private getExpressApplication : () => ExpressApplication,
private getRestifyApplication: () => RestifyApplication,
private hapiApplication : () => HapiApplication) {
console.log("NodeJsFramework ctor");
let frameworks = ["Express Application", "Restify Application", "Hapi Application"];
this.frameworkInstances = {};
let i = 0;
for(let argument of arguments) {
let typeName:string = frameworks[i++];
this.frameworkInstances[`${typeName}`] = argument;
}
}
public createNodeJsFramework(restFramework: string) : any {
return (restFramework) ? this.frameworkInstances[_.startCase(`${restFramework} Application`)] ():
this.frameworkInstances["Express Application"] ();
}
}
Conclusion
In section 2 of part 6, we have seen how to extract the routes from the NodeJsApplication classes for the 3 popular NodeJs Web frameworks: Express, Hapi Restify and create router classes for each of them. Then, we have seen how to use Aurelia’s DI container to inject the dependencies via constructor using @inject decorator instead of constructing them explicitly using new operator. We have also seen how to use Lazy resolver to defer the creation until it is required using a function returned by DI instead of an instance itself.
In section 3 of part 6, I’ll show you how you can have both ASP.NET Core 1.0 and NodeJs backend REST APIs and make them work with the same Aurelia front end web application in a single folder structure but using 2 instances of Visual Studio Code without duplicating the Aurelia front end web application source code. Happy Coding!
This is the first section of the sixth part of Building a web application using ASP.NET Core 1.0, Node.Js, Aurelia, TypeScript, Webpack in Visual Studio Code.
The source code for this tutorial is available on GitHub.
In this part, I’m going to show how to quickly setup Node.Js, TypeScript, Visual Studio Code and create a REST service using them. We are going to build a ContactManager REST service API in Node.Js using the three popular NodeJs Web frameworks: Express, Hapi and Restify, which we’ll consume from a modern SPA application built using Aurelia, TypeScript and WebPack. I’ll also show you how you can leverage Aurelia’s DI container on the server side inside NodeJs as well as how we can re-use our client side repository interface and InMemoryRepository implementation on the server side.
Installing the Pre-requisites
Before we start installing the pre-requisites, let us create a folder structure to start building our Node.JS REST API Service. Create a folder structure using this command:
mkdir C:\tutorial\part6\modern\nodejs\server\src
and then set the directory as the current directory using this command:
cd C:\tutorial\part6\modern\nodejs\
To install Node.Js, visit nodejs.org and download the Current version. At the time of writing this blog post, the Current version of Node.js is v6.6.0. Node.js is built using Chrome’s V8 JavaScript engine. Microsoft created a project that enables Node.js to optionally use the Microsoft’s ChakraCore JavaScript engine, which is powerful. Checkout the github page of Node.js on ChakraCore here. If you don’t want to spend time compiling Node.js with ChakraCore source code, you can download the releases from here.
Node.js comes with a powerful package manager named NPM (Node Package Manager). We can use npm to install both server and client JavaScript libraries. It is always a good idea to update npm using npm :) It might sound funny but I really mean it because npm has frequent releases than Node.js itself. To update npm, run the command:
npm install npm --g
Before we start installing other node modules, let us create a package.json by running this command:
npm init -y
Since we are going to use TypeScript to develop Node.Js applications, let us install TypeScript 2.0 RTM Release by running the command:
Since TypeScript requires the type definition files for JavaScript libraries, let us install the typings tool by running the command:
npm install typings --g
Let us create typings.json using this command:
typings init
Let us create a tsconfig.json file that will contain the settings required by TypeScript compiler. To create tsconfig.json, run the command:
tsc --init
To develop a REST service, we need a good web framework. Express is a fast, unopinionated, minimalist web framework for Node.Js. To install express, please run the command
npm install express body-parser --save
Now, let us install the type definitions required for Express by running the following command:
Note: Many node modules ship their type definitions in @types namespace and hence you can use npm install @types/<module_name> instead of typings install dt~module_name.
Restify is a very popular NodeJs framework and focus only on providing REST service endpoints unlike Express, which is a full blown web framework that you could use to serve side web pages as well as REST service endpoints. To install restify, please run the command:
npm install restify bunyan --save
Now, let us install the type definitions required for Restify by running the following command:
Since we’ll need some string manipulation functions from lodash, let us install lodash using the command:
npm install lodash @types/lodash --save
Creating Project Structure in Visual Studio Code
After we installed the pre-requisites, let us launch VS Code by changing to the project directory in Cmd.exe or Cmder (I prefer the latter) and by running the command:
code .
Make sure to modify the settings in tsconfig.json to look like this:
It is time to configure the Task Runner for “TypeScript –tsconfig.json” project type by typing Ctrl + Shift + B and follow the on-screen instructions below:
After we click on “Configure Task Runner” button and select “TypeScript – tsconfig.json Compiles a TypeScript project” as our Task Runner, VSCode will generate a tasks.json specific to this project type. We don’t need to modify the default values because they are good enough for our project. Once we configure the Task Runner, Ctrl+Shift+B is the key to remember to start/stop i.e., it acts as a toggle command to start the task runner.
Invoke the task runner by clicking Ctrl+Shift+B and you should see that the TypeScript compiler will be happy to compile without any errors and watching the file system for any changes you make to compile the modified files like this:
Now, let us start creating our application by adding a folder named “src” and a file named index.ts like this:
As soon as you added the file index.ts, our task runner which is watching for changes, transpiles index.ts into index.js and index.js.map since we chose to generate source maps as well. While it is great to see the generated JavaScript source and source map files to understand that the TypeScript compiler is working, it becomes annoying over a period of time to see 2 extra files in the folder for each .ts file you create. Visual Studio has a nice feature to hide the unwanted files using “File | Preferences | Workspace Settings” menu option. When you select that menu option, we should see a settings.json file under .vscode folder.
To make VS Code use TypeScript 2.0.3, add this setting in .vscode\settings.json file:
"typescript.tsdk": "node_modules/typescript/lib"
We need to add the “files.exclude” setting to hide .js and .js.map files:
// Place your settings in this file to overwrite default and user settings. { "typescript.tsdk": "node_modules/typescript/lib",
// Files
// Configure glob patterns for excluding files and folders. "files.exclude": { "**/*.map": {"when": "$(basename)"}, "**/*.js": {"when": "$(basename).ts"} } }
After you make the changes in settings.json, in order for VS Code’s language service to use TypeScript 2.0.3 installed locally under node_modules/typescript/lib folder, you need to restart VS Code.
I don’t like to see the src folder messed up with bunch of .js and .js.map files alongside .ts files. I’d like to keep it clean and also be able to use another editor/IDE like Visual Studio 2015 or 15 to run the project. So, a better approach to organize src and output files is to create a dist folder and configure TypeScript compiler to output the .js and .js.map files into the dist folder using “outDir” setting inside tsconfig.json like this:
We need to make some changes to .vscode/launch.json in order for VS Code debugger to pickup the right source map files so our breakpoints would work just fine. In launch.json , under “configurations” settings, we need to modify “program”, “sourceMaps” and “outDir” settings like this:
Setting up launch.json to run our Node.Js REST service from VS Code
To run an application from VS Code, simply hit F5 and in the “Select Environment”, choose Node.js. VS Code will create a launch.json under .vscode folder with the configuration required to launch a NodeJs application.
Open launch.json under the folder .vscode and change the “program” property to point to “server.ts” and and “sourceMaps” property to true. If you don’t make these changes, you can spend hours or days to figure out why aren’t our breakpoints not working. Our launch.json should look like this:
Creating a Contacts REST API using Express, Hapi and Restify frameworks
Since we’ll be creating this application class for all 3 frameworks: express, hapi and restify, let us define a TypeScript interface name INodeJsApplication under the folder named “bootstrap”.
import {ExpressApplication} from "./contact-manager/bootstrap/express-application";
export class NodeServer {
constructor() {
console.log("NodeServer ctor");
}
private normalizePort(val: string) : any {
"use strict";
let port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
bootstrap() {
// get port from environment and store in Express.
let port = this.normalizePort(process.env.PORT || 8080);
let restFramework = process.env.REST_FRAMEWORK || "express";
let server = new ExpressApplication();
server.bootstrap(port);
}
}
let nodeServer = new NodeServer();
nodeServer.bootstrap();
Debugging our REST service endpoint in VSCode
Now that we have completed application.ts and server.ts, let us try running it from within VS Code. Let us set a breakpoint on line 75 in application.ts that reads “response.json(contacts);”. We can hit f5 and that should launch the debugger in VS Code using launch.json settings. Then, open up chrome browser and enter http://localhost:8085/api/contacts to see the control comes to the breakpoint inside of VS Code and you can inspect the variables by a simple hover on any variable or switching to the Debug View by pressing Ctrl+Shift+D to inspect variables, call stack etc. like this:
Once we hit Continue i.e., F5, the function will return the JSON value of our contacts array with 4 contact objects. We can see the JSON returned from our API inside of the browser like this:
Organizing our project for cleaner separation
It is not a good idea to create a single Application class and bloat the class with our API implementation methods, routes etc. If you noticed, our get() method creates an array of contacts, which we’ve already implemented using an InMemoryContactsRepository on the client side. Since NodeJs can leverage the client side JavaScript code that we’ve written provided we didn’t do any DOM manipulation or UI specific code, we can use that InMemoryContactsRepository on the server side as well.
Let us organize our project for a cleaner separation. The first and foremost thing is defining our application Model classes that are like our business entities. For a Contacts REST API, it is a no-brainer that we need to define the Contact model. We are going to leverage the power of TypeScript interface to define the Contact Model. We’ll create a folder structure for our Contacts REST API like this:
Then, we’ll copy the client side code under “models” and “services” folders into the server side. Here’s the definition of Under models folder, define IContact like this:
The next step in organizing our project is to create a “services” folder and add the interface and classes that will wrap our business logic. For this project, we will define IContactService interface like this:
Before we start defining the concrete implementation classes implementing IContactService, let us see the importance of Promise classes in modern JavaScript applications.
Using Promises in Node.js
In JavaScript, when we call a method that runs asychronously i.e., a method that performs an operation for longer time but returns right away and eventually after it is successfully completed or failed would call the success and error callback functions. We can write a complicated and messy code by adding callbacks, which is commonly called as “callback hell”. In ES6, they have introduced Promise classes that makes our asynchronous code look like synchronous code with flat indentation and one exception channel (i.e., error handling code). Nodejs does not support ES6 promises yet. So we can use TypeScript to bring in ES6 Promise classes or better yet use the powerful Bluebird Promise library for all of our Promise implementations. Since the repository code we brought from the client side already uses Bluebird, we are going to use the Bluebird Promise library on the server side as well. Let us install Bluebird and its type definition using this command:
npm install bluebird @types/bluebird --save
Let us look at a simple in memory implementation of IContactService that we brought from our client side code:
InMemoryContactService implements the CRUD (Create, Read, Update and Delete) methods besides the search() method. All these methods on the internal array of contacts in memory.
If you notice, we are using findIndex method, which is defined in Array.prototype and is only available in ES6. This is why set the “target” setting to “es6” in our tsconfig.json. Let us add another useful setting named “allowJs” into our tsconfig.json like this:
Setting “allowJs” to “true” allows us to pass through JavaScript files that are untyped modules. “allowJs” comes in handy when you are migrating your JavaScript code to TypeScript and would like the TypeScript compiler to compile your js files. Remember that you can’t use type annotations on modules imported without type definition files i.e., d.ts.
Refactoring ExpressApplication to use InMemoryContactService
We need body-parser module to parse the body of the http request to handle POST, PUT and DELETE http verbs. We have already installed body-parser when we installed express and its dependent modules.
We need to import the bodyParser, IContact and InMemoryContactService classes like this:
import * as bodyParser from "body-parser";
import {IContact} from "../models/icontact";
import {InMemoryContactService} from "../services/in-memory-contact-service";
We need to let express use the bodyparser inside our configApiRoutes() method in application.ts like this:
"use strict";
import * as Hapi from "hapi";
import * as Boom from "Boom";
import {INodeJsApplication} from "./inodejs-application";
import {IContact} from "../models/icontact";
import {InMemoryContactService} from "../services/in-memory-contact-service";
export class HapiApplication implements INodeJsApplication {
public hapiApplication: Hapi.Server;
private contactService: InMemoryContactService;
constructor() {
console.log("HapiApplication ctor");
// create expressjs application
this.hapiApplication = new Hapi.Server();
}
private configErrorRoutes() {
// catch 404 and forward to error handler
this.hapiApplication.ext("onPreResponse", (request: Hapi.Request, reply: Hapi.IReply) => {
if(request.response.isBoom) {
if(request.response.output.statusCode === 404) {
console.log("Invalid Url");
return reply(Boom.notFound("check your url"));
}
return reply(request.response);
}
return reply.continue();
});
}
public configApiRoutes() {
var endpoint = "/api/contacts";
this.hapiApplication.route({
path: endpoint,
method: "GET",
handler: (request: Hapi.Request, reply: Hapi.IReply) => {
this.contactService.getAll().then((contacts : IContact[]) => {
reply(contacts);
});
}});
}
public bootstrap(port: number) {
this.hapiApplication.connection({port: port});
// configure error routes
this.configErrorRoutes();
// configure API routes
this.configApiRoutes();
this.hapiApplication.start(() => {
console.log("Listening on " + this.hapiApplication.info.uri);
});
}
}
Creating a GET All Contacts API endpoint in Restify
restify-application.ts
"use strict";
import * as restify from "restify";
import {INodeJsApplication} from "./inodejs-application";
import {IContact} from "../models/icontact";
import {InMemoryContactService} from "../services/in-memory-contact-service";
export class RestifyApplication implements INodeJsApplication {
private restifyApplication: restify.Server;
private contactService: InMemoryContactService;
constructor() {
console.log("RestifyApplication ctor");
// create restify server
this.restifyApplication = restify.createServer();
restify.CORS.ALLOW_HEADERS.push("authorization");
this.restifyApplication.use(restify.CORS());
this.restifyApplication.use(restify.pre.sanitizePath());
this.restifyApplication.use(restify.acceptParser(this.restifyApplication.acceptable));
this.restifyApplication.use(restify.bodyParser());
this.restifyApplication.use(restify.queryParser());
this.restifyApplication.use(restify.authorizationParser());
this.restifyApplication.use(restify.fullResponse());
// configure error routes
this.configErrorRoutes();
// configure API routes
this.configApiRoutes();
}
private configErrorRoutes() {
// catch 404 and forward to error handler
this.restifyApplication.on("NotFound", function(request: restify.Request, response: restify.Response,
erorr: restify.HttpError, next: restify.Next) {
console.log("Invalid Url");
var customError = new Error("Not Found");
response.send(404, customError);
return next();
});
}
public configApiRoutes() {
var endpoint = "/api/contacts";
this.restifyApplication.get(endpoint, ((request: restify.Request, response: restify.Response, next: restify.Next) => {
this.contactService.getAll().then((contacts : IContact[]) => {
response.json(contacts);
return next();
});
}));
}
public bootstrap(port: number) {
let server = this.restifyApplication;
// listen on provided ports
server.listen(port, () => {
console.log("%s listening at %s", server.name, server.url);
});
}
}
Wiring all 3 frameworks in node-server.ts
Let us import the HapiApplication and RestifyApplication classes like this:
import {HapiApplication} from "./contact-manager/bootstrap/hapi-application";
import {RestifyApplication} from "./contact-manager/bootstrap/restify-application";
It is time for us to use that restFramework variable we defined inside of bootstrap() method. That variable will check if there is an environment variable named “REST_FRAMEWORK” passed as an argument to Node. If a value is passed, it will use that value; othwerise, it will default to “express”. Let us use a switch..case statement to check the value of restFramework and create an instance of one of the frameworks: express, hapi or restify like this:
bootstrap() {
// get port from environment and store in Express.
let port = this.normalizePort(process.env.PORT || 8080);
let restFramework = process.env.REST_FRAMEWORK || "express";
let server = null;
switch(restFramework) {
case "express":
server = new ExpressApplication();
break;
case "hapi":
server = new HapiApplication();
break;
case "restify":
server = new RestifyApplication();
break;
}
server.bootstrap(port);
}
Creating a Class Factory to abstract the creation of these 3 frameworks
NodeJsFrameworkFactory.ts
import {ExpressApplication} from "./express-application";
import {RestifyApplication} from "./restify-application";
import {HapiApplication} from "./hapi-application";
import * as _ from "lodash";
export class NodeJsFrameworkFactory {
private frameworkInstances: any[];
constructor() {
console.log("NodeJsFramework ctor");
this.frameworkInstances = new Array(arguments.length);
this.frameworkInstances["express Application"] = new ExpressApplication();
this.frameworkInstances["hapi Application"] = new HapiApplication();
this.frameworkInstances["restify Application"] = new RestifyApplication();
}
public createNodeJsFramework(restFramework: string) : any {
return (restFramework) ? this.frameworkInstances[_.startCase(`${restFramework} Application`)] :
this.frameworkInstances["expressApplication"];
}
}
Refactoring NodeServer to use NodeJsFrameworkFactory
We will remove the import statements for ExpressApplication, HapiApplication and RestifyApplication and just import NodeJsFrameworkFactory class in node-server.ts like this:
import {NodeJsFrameworkFactory} from "./contact-manager/bootstrap/nodejs-framework-factory";
We’ll replace the switch..case statement inside bootstrap() method like this:
let server = this.nodejsFrameworkFactory.createNodeJsFramework(restFramework);
Changing REST_FRAMEWORK environment variable to test all 3 frameworks
We have completed wiring all 3 popular NodeJs frameworks into our NodeServer class. Now, we can add REST_FRAMEWORK environment variable in launch.json to test all 3 frameworks like this:
To test Hapi and Restify frameworks, we need to just change the REST_FRAMEWORK to "hapi" or "restify" in launch.json.
Conclusion
In section 1 of part 6 of the series, we have covered a lot of Node.Js server side concepts and used three popular web frameworks: Express, Hapi and Restify to implement an API endpoint using HTTP GET. In section 2 of part 6 of this series, we’ll see how you can refactor the routes out of the application classes and use DI (Dependency Injection) to inject the NodeJsFrameworkFactory, Application, Service and Route classes and use them from NodeServer class. Happy coding!