Creating a MEAN Stack with Angular 2 and TypeScript

Tyler Smith
7 min readNov 4, 2016

--

With the release of Angular 2 earlier this year, many people are going to be starting new projects with Angular 2 and Node.js. A lot of people will decide that they want to use TypeScript for their whole stack, not just the Angular code.

When I went to do this for my own project, I found that the project structure was causing some issues with the way TypeScript and Angular 2 are set up. Before I dive into the details, though, I want to highlight the issues that I will be addressing.

Who This Article is For

This article will not be going over the details of how to use TypeScript, Angular 2, or Node.js. There are many helpful tutorials on the Internet for that purpose; rather, this article will be touching on the difficulties that arise when trying to combine these technologies.

These problems will only arise if you wish to keep your Angular 2 code and Node.js code in the same project with a single build process. If you have two separate repositories, you will not run into any of these issues because they will be completely independent of each other.

The Problem

You may be wondering why Angular 2 would be any harder to insert into your project than Angular 1. For the most part, you can simply insert the Angular 2 code in the same place in your folder structure as you used to, but there are two issues that I ran into:

  1. Both Angular 2 and Node.js have dependencies installed through NPM
  2. If you are using TypeScript for your entire project, not just the Angular 2 code, you will run into typings collisions

Setting Up Your Project Structure

You can use whatever Node.js/Express folder structure you are used to; just put your Angular 2 code where you would usually put your front end (perhaps in a folder called public). Here is an example of a popular project structure for Node.js and Express:

The Starting Project Structure

Like I said, there is no requirement to use this project structure, but if you are, here are a few important things to note:

  • The client and server code are separated into two independent directories. This makes setting up the build process for TypeScript a lot easier.
  • The package.json file is at the root of the project. This allows us to have one single build flow (and is the source of the issues we are tackling in this article). It can cause some complication, but it makes things a lot easier as we are developing and bringing on new team members. They will be able to do commands like npm install and building just once from the root directory of the project.
  • I have included a gulpfile.js file. For this project, I will be using Gulp to handle doing two separate TypeScript build processes — one for the client and another for the server — with a single command from the root of the project.

Creating the Two TypeScript Projects

At this point, you probably think we should put a tsconfig.json and a typings.json file at the root of the project; however, that will lead to one of the problems I mentioned earlier: a collision in the Typings definitions.

Unfortunately, since Angular 2 uses polyfills (at least, if you structure it how they do in the official Angular 2 tutorial), there will be multiple definitions that define the same symbol, which the TypeScript compiler doesn’t like.

For those of you that don’t know what a Typings definition is, it is basically like a dictionary that tells the TypeScript compiler about all the types involved in a library, including return types, parameter types, and properties for different library-specific types. For example, you can’t take advantage of TypeScript’s features with Mongoose without downloading the Mongoose Typings definition, which tells TypeScript about the existence of different functions in the Mongoose library, such as findById.

The easiest way I have found to deal with this issue is to create two tsconfig.json and typings.json files, but keep them as one build process with Gulp.

Your project structure should now look like this:

The Separate TypeScript Files

The contents of these files will depend on the libraries you are using and how you want the TypeScript compiler to act, but make sure you set up your excludes property. If you don’t know about these files, a good base one is provided in the official Angular 2 tutorial, or check out the documentation for TypeScript config files.

Bringing the Two TypeScript Projects Together with Gulp

Now we have two independent TypeScript projects, one for the client and one for the server, but we only have one repository and we want to be able to do everything from the root folder with one package.json file. This is where Gulp comes in.

For those of you who have never used Gulp, it is a powerful build tool that allows you to write basic build scripts, or “tasks,” and combine them. For example, you could setup your script so that running gulp typescript will build all your .ts files to .js files.

Let’s start by downloading our dependencies, run the following commands:

npm install --save-dev gulp gulp-typescript
npm install -g gulp

This will install two local dependencies that we will use in our gulpfile.js, as well as installing Gulp globally so that you can use the gulp command from the terminal.

Start by adding the following to your gulpfile.js:

Note: This is the one script file in our project that is .js instead of .ts, because this script is the file that handles the TypeScript transpiling (one day it will transpile itself, but not today). We can still use the nice ES6 features, such as the arrow functions or the “let” keyword, by including the ‘use strict’ at the top of the file.

Optional: Source Maps and Minification

By simply running gulp at a terminal, we can now transpile all our TypeScript code into JavaScript. The scripts in client/src will be built to client/app, and the scripts in server/src will be built to server/app.

However, you will most likely want to take this further by generating source maps and minifying the output. If you look at one of the output JavaScript files right now, you will notice it is ugly, but on multiple lines with indentation. Minification makes the output even uglier by putting the entire file on one line, saving file size, which is especially important for the client code.

To add minification to the project, start by installing the dependency:

npm install --save-dev gulp-uglify

Now add it to your gulpfile.js by adding the following alongside the other imports:

let uglify = require('gulp-uglify');

Additionally, add a call to it to both the clientscripts and serverscripts tasks, as following:

Okay, so now our JavaScript looks extremely ugly, and nobody is going to read it in that state, so now how are you supposed to debug? This is where source maps come in, they act as a bridge between the JavaScript and the TypeScript. Applications, such as your browser, can use the source maps to figure out where the JavaScript came from. If your application has an error, the browser can use the source map to figure out what line of the original TypeScript file caused the error.

Adding source maps to your project is as simple as adding another library to the Gulp pipeline; let’s start by installing the library:

npm install --save-dev gulp-sourcemaps

Now you can import it to your Gulp file:

let sourcemaps = require('gulp-sourcemaps');

The pipeline for this one is a bit more complex because you have to tell the sourcemaps library when to start and when to stop, instead of just calling it once. Add it to both tasks with the following addition:

Success! We have just finished setting up our TypeScript build process. Since we have two separate project, we will not have any issues with the client and server overlapping each other. Also, by using Gulp, we have completed made it feel like only one project to the developer.

Adding an NPM Start Script for Development

Since we added that “watch” task to the Gulp file, we can set up an NPM script that will start the application and run with live reloading.

Before doing this, we will need two global dependencies:

npm install -g concurrently nodemon

Now, update your package.json file:

"scripts": {
"start": "gulp && concurrently \"gulp watch\" \"nodemon server/app/server.js\""
}

This script will do run Gulp once at the beginning, then start the application while watching. gulp watch looks for changes in your TypeScript and updates the JavaScript, accordingly. Nodemon is a library that starts our Node server and looks for changes in the JavaScript files, refreshing the server, as needed.

We can now build and start our application with:

npm start

Note: Some people prefer to add other NPM scripts, such as “build” and “build:watch,” that run “gulp” and “gulp watch,” respectively. This may not seem any different, but it abstracts the fact that you are using gulp. You could swap out Gulp for something else and update the NPM scripts to match.

Dealing with the node_modules Folder

Our last problem is a simple one, but could trip you up; especially if you are new with Express. When you add your Angular 2 project to the client/src folder, you will get errors in the browser due to the fact that the node_modules folder is outside of the client/ folder.

In the Angular 2 tutorial, they have their node_modules folder right inside the public folder (the entire project is the public folder in that tutorial). This is an easy fix; we just need to make the node_modules folder public.

Somewhere in your Express app, you will have a line similar to this one to set the public folder:

app.use(express.static('./client');

This line maps the client folder to the root of the static assets. This means, if you were to visit http://localhost:<port>/, you will be shown the index.html file from the client folder.

We can have more than one of these statements without them overwriting each other. To bind the node_modules folder to http://localhost:<port>/node_modules/, add the following line directly above the last one:

app.use('/node_modules', express.static('./node_modules'));

--

--

Tyler Smith

I am a full stack developer who specializes in web and mobile applications.