facebook

Blog

Stay updated

A simple startup project to create an Angular application that use NodeJS and Typescript for the backend
Angular, NodeJS and Typescript together
Wednesday, October 24, 2018

A new customer called me for a project on an embedded device, where the purpose is to provide a simple user interface to control the execution of a workflow on the device. After discarding Asp.Net Core, because it does not support ARM processor at the moment, we proposed Angular for the front-end and Node for the backend. The real execution of the workflow is demanded to a core project written in C++, of course: our job is to create a user interface to create and display the workflow.

The customer accepted the use of Node but, since he knows the .Net platform, he asked us to use Typescript for the backend too. This is not a problem, but I think it can be interesting to share the setup of an Angular project with Node and Typescript.

As a first step, we install the prerequisites:

  • NodeJS from https://nodejs.org/
  • Angular CLI with the command npm install -g @angular/cli
  • Typescript with the command npm install -g typescript
  • Gulp with the command npm install -g gulp

We create a new project with the Angular CLI (for example, with the command ng new angular-node-typescript), and open the project with our preferred code editor, in my case Visual Studio Code: https://code.visualstudio.com

We create a folder “server” in the project root, where we place the backend code, and create in that folder a new file named server.ts. As framework for the NodeJS backend we use ExpressJS, which can be installed in our project with following commands:

  • npm install –save express for ExpressJS, the –save option add this required package to our configuration in package.json
  • npm install –save body-parser as ExpressJS middleware. Its role is to extract the entire body portion of an incoming request stream and expose it on req.body. Very useful.
  • npm install @types/node @types/body-parser @types/express -D, for the Typescript definition files for node, body-parse rand express. The -D option saves these dependencies as development dependencies, because they are useful only during the development process (in package.json we will find these dependencies in the devDependency block)

Return to the server.ts file and import the necessary library:

import * as express from 'express'; 
import * as path from 'path'; 
import * as http from 'http'; 
import * as bodyParser from 'body-parser';

Now we can create the Server class:

class Server { 
    public app: express.Application; 
 
    public static bootstrap(): Server { 
        return new Server(); 
    } 
 
    constructor() { 
        // create expressjs application 
        this.app = express(); 
    } 
}

And call the static method bootstrap to create the Server object:

Server.bootstrap();

Now we can init our web server, creating a private method of this class, named config. Here we configure body-parser middleware, set the public folder where we will publish the Angular project, configure the port where the server will listen for HTTP request and start the server:

private config() { 
    // Parsers for POST data 
    this.app.use(bodyParser.json()); 
    this.app.use(bodyParser.urlencoded({ extended: false })); 
 
    // Point static path to public folder 
    this.app.use(express.static(path.join(__dirname, 'public'))); 
  
    /** 
     * Get port from environment and store in Express. 
     */
    const port = process.env.PORT || '3000'; 
    this.app.set('port', port); 
 
    /** 
     * Create HTTP server. 
     */
    const server = http.createServer(this.app); 
 
    /** 
     * Listen on provided port, on all network interfaces. 
     */
    server.listen(port, () => console.log(`API running on localhost:${port}`)); 
}

Our backend is a collection of REST web api, so we need to configure the routes of the project. For this purpose, we can create a second private method in Server class, named routes:

private routes() { 
    // get router 
    let router: express.Router; 
    router = express.Router(); 
 
    // create routes 
    const api: BackendApi = new BackendApi(); 
 
    // test API 
    router.get('/api/test', api.test.bind(api.test)); 
 
    // use router middleware 
    this.app.use(router); 
 
    // Catch all other routes and return the index file 
    this.app.get('*', (req, res) => { 
        res.sendFile(path.join(__dirname, 'public/index.html')); 
    }); 
}

As we can see, in the code we use the ExpressJS library middleware router to improve the maintenance of the project. We place the API implementation in a subfolder named routes. For our example, we can create a file backendapi.ts in this folder:

import * as express from 'express'; 
 
export class BackendApi { 
    public test(req: express.Request, res: express.Response) { 
        res.json('Hello World'); 
    } 
}

Just a note on the last instruction of the routes method:

// Catch all other routes and return the index file 
this.app.get('*', (req, res) => { 
    res.sendFile(path.join(__dirname, 'public/index.html')); 
});

This note permits to answer with the built Angular application to all the requests that have not been managed in another way. The Server constructor become, then, as follows:

constructor() { 
    // create expressjs application 
    this.app = express(); 
 
    // configure application 
    this.config(); 
 
    // configure routes 
    this.routes(); 
}
constructor() { 
    // create expressjs application 
    this.app = express(); 
 
    // configure application 
    this.config(); 
 
    // configure routes 
    this.routes(); 
}

The server is ready: if we move into a server directory and run the command tsc server.ts, the Typescript compiler creates the server.js file, that, executed in node with the command node server.js, starts our server:

If we open the browser at the address http://localhost:3000/api/test, we can display the right result:

Now we can modify the Angular application to call the service. In app.module.ts, which is present in the src/app folder, we import the HTTP client module:

import { BrowserModule } from '@angular/platform-browser'; 
import { NgModule } from '@angular/core'; 
import { HttpClientModule } from '@angular/common/http'; 
import { AppComponent } from './app.component'; 
 
@NgModule({ 
    declarations: [ AppComponent ], 
    imports: [ BrowserModule, HttpClientModule ], 
    providers: [], 
    bootstrap: [AppComponent] 
}) 
export class AppModule { }

In app.component.ts, we use the HTTP client to call the service:

import { Component } from '@angular/core'; 
import { HttpClient } from '@angular/common/http'; 
 
@Component({ 
    selector: 'app-root', 
    templateUrl: './app.component.html', 
    styleUrls: ['./app.component.css'] 
}) 
export class AppComponent { 
    title = 'app'; 
 
    constructor(httpClient: HttpClient) { 
        httpClient.get('http://localhost:3000/api/test')
            .subscribe(arg => this.title = arg); 
    } 
}

Obviously, this is only an example, we will never call a service directly from a component, right?

Launching the angular app with the command ng serve -o, we can see the result in the browser:

Ok, it works. But if we have to work on a real project, it’s important to automate all the tasks necessary to run and test our application: therefore, we configure Gulp to run all the needed tasks. Gulp is a famous task runner, very simple to use because we can configure it using JavaScript. You can find all the documentation on the official web site: https://gulpjs.com/

We install Gulp in the project with the command npm install gulp -D. We need also some Gulp plugins, one for the Typescript compilation, gulp-typescript, e one for the Node server execution, gulp-nodemonnpm install gulp-typescript gulp-nodemon -D.

Now create in the project root a file named gulpfile.js, the Gulp configuration file, and start importing the dependencies:

var gulp = require('gulp'), 
    ts = require('gulp-typescript'), 
    exec = require('child_process').exec, 
    nodemon = require('gulp-nodemon'); 

The exec object will be useful to run the Angular CLI commands. Create a task in Gulp is very simple, we have only to call the task method with two parameters: the name of the task and the function that will be executed when the task is invoked by its name. For example, for the typescript configuration of the backend code, we will write:

gulp.task('backend', function () { 
    return gulp.src('./server/**/*.ts')
        .pipe(ts({ noImplicitAny: true, lib: ["es2015"] 
    }))
    .pipe(gulp.dest('dist/')); 
});

The task is named ‘backend’, and in the callback function we take all the file in the server folder and serve them, with the pipe function, to the typescript compiler, moving the result of the compilation in a folder named dist. Now, if we run in the terminal window the command gulp tsc, the task is invoked and executed.

Now we create a new task, named frontend, in the Gulp configuration file for the Angular application build:

gulp.task('frontend', function (cb) { 
    exec('ng build --prod -op=dist/public', function (err, stdout, stderr) { 
        console.log(stdout); 
        console.log(stderr); 
        cb(err); 
    }); 
})

If we run the command gulp frontend in the terminal, the Angular application will be built in the folder dist/public. It’s time to run the application, then we create a new task named ‘nodemon’:

gulp.task('nodemon', function () { 
    nodemon({ 
        script: 'dist/server.js', 
        ext: 'js', 
        env: { 'NODE_ENV': 'development' } 
    }) 
});

Nodemon is a NodeJS monitor: it executes the script dist/server.js and restarts the node server if the file changes. In the terminal, we execute gulp nodemon, to start the application:

Now our frontend is available on the same port of the backend because is served by the backend:

But we are perfectionist, so create a new Gulp task to recompile the backend if some Typescript file change:

gulp.task('watch', ['backend'], function () { 
    gulp.watch('./server/**/*.ts', ['backend']); 
});

And, since if we create a task with the name default we can call it with the command gulp without parameter, we can create the last task to call all the already created tasks :

gulp.task('default', ['frontend', 'backend', 'watch', 'nodemon']);

In this way, we have only to run gulp in the terminal! All the code is available on my GitHub: https://github.com/apomic80/angular_node_typescript

See you soon