A Modern MEAN-stack with Angular and Typescript
Tags: node, typescript, angular, mongodb
Published: 2021-01-21 23:03:50

---
layout: post
title: A Modern MEAN-stack with Angular and Typescript
subheading: Follow these steps to quickly set up a full-stack Typescript project with Mongo, Express, and Angular.
published: true
description: Follow these steps to quickly set up a full-stack Typescript project with Mongo, Express, and Angular.
tags: 
 - node 
 - typescript 
 - angular 
 - mongo
---
![Construction Site](https://dev-to-uploads.s3.amazonaws.com/i/g87vyuc3io65x0xklouv.jpg)
###### Photo by [Tolu Olubode](https://unsplash.com/@toluobde?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText) on [Unsplash](https://unsplash.com/s/photos/construction?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText)

## Introduction
One of the advantages I love about using [Angular](https://angular.io/) is that the framework is truly ["batteries included"](https://chariotsolutions.com/presentation/single-page-application-day-angular-the-batteries-included-enterprise-spa-framework-rich-freedman/). From the application architecture, to configuration, to third-party libraries, to testing setup, to extra compilation tools, it's a set of really smart decisions that help get a fully featured browser application running quickly. For tasks like setting up scripts, compiling Typescript, CSS preprocessing, Webpack, and testing, the Angular CLI can save lots of tedious configuration.

 Likewise, for independent projects, [Node.js](https://nodejs.org/en/) and [Express](https://expressjs.com/) can be great choices as they tend to be easy to deploy to a variety of platforms. 

Combined with [MongoDb](https://www.mongodb.com/1) for data persistence, these tools have long been known as the [MEAN Stack](https://en.wikipedia.org/wiki/MEAN_(solution_stack)) (with [AngularJS](https://angularjs.org/) being the original _A_ in MEAN), with "Javascript everywhere" being the unifying idea. 

Over a few months of trying out different Angular-Express-With-Typescript setups, I've come up with a way to set up these kinds of projects using the [Angular CLI](https://cli.angular.io/) as the starting point. After creating an Angular-based workspace, I can add an Express.js application written in Typescript, and configure the two to transpile Typescript files to a single deployable Node.js web app. With this set up, we can also use [Nodemon](https://nodemon.io/) and [Concurrently](https://www.npmjs.com/package/concurrently) to create a convenient dev workflow similar to what the Angular CLI provides with the `ng serve --open` command.

This article will be the first in a two-part series. In part one, we'll go through setting up and configuring Angular and Express. Then we'll use Nodemon, Concurrently, and [live-server](https://www.npmjs.com/package/live-server) to handle compiling and refreshing the application when changes are made to the code base. 

In another tutorial, I'll show how we can use Docker to serve the development database, a setup that's a little more flexible and convenient than running MongoDb locally (although that's perfectly fine too).

## Prerequisites
This tutorial will assume at least some familiarity with Angular and the Angular CLI, Typescript and its CLI, and Express. 

The following tools should be installed before starting (the links are to their respective "Getting Started" pages).
 - [Node.js](https://nodejs.org/en/download/) - I'm using version 14.15.1 as I write this.
 - [Angular CLI](https://angular.io/cli) - I'm using version 11. If you're a few version behind, these steps should still work.
 - [Typescript](https://www.npmjs.com/package/typescript) - I'm using version 4.

We'll also install a couple of NPM tools globally, but I'll explain those as we come to them. 

## Set up an empty Angular project
The first step will be to use the Angular CLI to set up an Angular workspace so that we can take advantage of all of the framework's smart default configurations from the start. 

Normally, we would use the `ng new` command to create the project which would scaffold a few application components and tests to get us going. However, in our first step, we're going to set up the workspace and the application separately.

Start with this command:
```bash
$ ng new NameApp --create-application=false --new-project-root=. --skip-install=true
```
Notice some new flags:
 - `--create-application=false` just sets up the workspace.
 - `--new-project-root=.` will help any configuration files (`tsconfig.json`, `angular.json`) find all of the locations in our project with minimal headaches.
 - `--skip-install=true` skips installing the `node_modules` packages. Since Angular comes with a ton of dependencies, we'll do our `npm install` all at once later on. This makes it easier to delete the entire project and start over if something doesn't turn out right.

Now we'll `cd` into the project directory and create the client application:
```bash
$ cd NameApp
$ ng generate application client --skip-install=true
```
You'll be prompted to select if you want to add routing to the project, and your preferred CSS library.

We just created the usual, scaffolded Angular directories in a `client` directory. Now we can keep separate directories for our client-side and server-side code. 

Note that if you add `--dry-run` or `-d` to the end of both of these command, this runs the command without actually adding new files, allowing you to see how the project will be layed out first, which is very convenient for experimenting with unconventional setups.

If all the directories look correct, run `npm install` to install all of the Node.js packages. 

With everything installed, run `ng serve --open` and test that the default application is working in a browser as expected. 

## Install some packages to support Express
Now that a basic browser application is working, we'll create an Express.js application that we'll write in Typescript. Everything will live in a directory called `server` and we'll structure it in a similar setup to a typical Javascript Express application.

In the project's root folder, we'll install the main dependencies we'll need:
```bash
$ npm install express
$ npm install --save-dev @types/express
```
We've added Express as our API server, and we've added the Typescript type definitions for Express.

Next, we'll set up some files and directories for the server-side code:
```bash
$ mkdir -p server/bin
$ touch server/app.ts
$ touch server/bin/www
```
All of the server-side code will live in `server`. The `bin/www` file is a typical entry point file for an Express app, and `app.ts` will be the root application file that will assemble all of the middleware for the API.

In an editor, open `bin/www` and paste the following:
```javascript
#!/usr/bin/env node

/**
 * Module dependencies.
 */

const app = require('../app').default();
const debug = require('debug')('NameApp:server');
const http = require('http');
/**
 * Get port from environment and store in Express.
 */

const port = normalizePort(process.env.PORT || '3000');
app.set('port', port);

/**
 * Create HTTP server.
 */

const server = http.createServer(app);

/**
 * Listen on provided port, on all network interfaces.
 */

server.listen(port, () => console.log(`Application is listening on port ${ port }`));
server.on('error', onError);
server.on('listening', onListening);

/**
 * Normalize a port into a number, string, or false.
 */

function normalizePort(val) {
  const port = parseInt(val, 10);

  if (isNaN(port)) {
    // named pipe
    return val;
  }

  if (port >= 0) {
    // port number
    return port;
  }

  return false;
}

/**
 * Event listener for HTTP server "error" event.
 */

function onError(error) {
  if (error.syscall !== 'listen') {
    throw error;
  }

  const bind = typeof port === 'string'
    ? 'Pipe ' + port
    : 'Port ' + port;

  // handle specific listen errors with friendly messages
  switch (error.code) {
    case 'EACCES':
      console.error(bind + ' requires elevated privileges');
      process.exit(1);
      break;
    case 'EADDRINUSE':
      console.error(bind + ' is already in use');
      process.exit(1);
      break;
    default:
      throw error;
  }
}

/**
 * Event listener for HTTP server "listening" event.
 */

function onListening() {
  const addr = server.address();
  const bind = typeof addr === 'string'
    ? 'pipe ' + addr
    : 'port ' + addr.port;
  debug('Listening on ' + bind);
}
```

This is almost exactly what gets generated when scaffolding a typical Express application with javascript and it basically pulls in our application code to create an HTTP server in the Node.js runtime. This file will most likely stay unchanged throughout any project.

Next, open `app.ts` and we'll paste in a very minimal Express setup:
```typescript
import * as express from 'express';
import { Express, Request, Response } from 'express';

export default function createApp(): Express {
    const app = express();
    app.get('/api/:name', async (req: Request, res: Response) => {
        const name = req.params.name;
        const greeting = { greeting: `Hello, ${ name }` };
        res.send(greeting);
    });
    return app;
}
```
Not much going on here. So far, what we can expect is that when we run the server, a `GET` request to `/api/Artie` will return `Hello, Artie`.

Now we need to process the Typescript file and output them as Javascript that the Node.js runtime can read.

## Setting up a build artifacts directory
Our intention is to output all of the Javascript code to a `./dist` directory in the root of the project. This is the directory that Angular normally compiles all of its browser code to, and it's already in the `.gitignore` that Angular created. We'll modify `./dist` to instead end up with a unified deployment package once the Angular and Express code is all processed. At the end, all of our Typescript scripts will be output to the `./dist` directory as follows:
```bash
/dist
 - /api
    - /bin/www
    - app.js
    # (... everything we made with Express ...)
 - /public
    # (... everything we made with Angular)
```
With the Angular code, we only need to make a small change to the `angular.json` file.
```javascript
{
  // ...
  "projects": {
    "client": {
      "architect": {
        "build": {
          "options": {
            "outputPath": "dist/public",  // <--- CHANGE THIS PATH
```
The rest of the Angular configurations should be okay as they are.

For our server-side code, we'll add a separate `tsconfig` file in the `./server` directory:
```bash
$ touch ./server/tsconfig.api.json
```
Add these values:
```json
{
    "compilerOptions": {
      "baseUrl": "../",
      "module": "CommonJS",
      "resolveJsonModule": false,
      "esModuleInterop": false,
      "target": "ESNext",
      "outDir": "../dist/api",
      "sourceMap": true,
      "types": [
        "node"
      ],
      "experimentalDecorators": true,
      "emitDecoratorMetadata": true
    }
  }
```
The important settings to note is that the `baseUrl` value is still the root of the project so that it draws from the same `node_modules` library. Then `outDir` is set to the same `./dist` directory where all of the compiled output goes. 

With our first `app.ts` iteration written, and our configurations updated, we now need to transpile the file to Javascript and make sure that the `bin/www` file can load it. We'll do the following test:

Create a `./dist` directory with an `api/bin` directory at the root of the project if there isn't one there already. Then copy the `www` file:
```bash
$ mkdir -p ./dist/api/bin
$ cp ./server/bin/www ./dist/api/bin
```
Now, with the Typescript compiler, we'll turn `app.ts` into Javascript output:
```bash
$ tsc -p ./server/tsconfig.api.json
```
Double check that it has been created `./dist/api/app.js`.

Run the `www` with Node to see if the Express.js server runs and accepts a test `GET` request as expected:
```bash
$ node ./dist/api/bin/www
```
In another terminal:
```bash
$ curl http://localhost:3000/api/Artie
```
And we should see `{"greeting" : "Hello, Artie"}` returned to the terminal. 

## Setting up scripts for unified client and server-side development
At this point you should have gotten signs of life from both the Angular and the Express apps. Now we need to combine the two so that we can serve the entire application on a single port. To do this, we'll set up Angular to build to the `./dist/public`, then set the Express server to serve the static files from that directory.

First, we'll set Express to serve static files from `./dist/public`. Here is `app.ts` with those lines added:
```typescript
import * as express from 'express';
import * as path from 'path';  // < -- add this
import { Express, Request, Response } from 'express';

export default function createApp(): Express {
    const app = express();
    const clientDir = path.join(__dirname, '../public');  // <-- add this 
    app.use(express.static(clientDir));                   // <-- and add this
    app.get('/api/:name', async (req: Request, res: Response) => {
        const name = req.params.name;
        const greeting = { greeting: `Hello, ${ name }` };
        res.send(greeting);
    });
    return app;
}
```
Note that the location of `public` is relative to the compiled `app.js` when it's in the `./dist/api` directory.  

Now, the following commands will 1) Build the static assets from Angular, 2) transpile the changes added to `app.ts`, and 3) serve the entire application from Express as before:
```bash
$ ng build
$ tsc -p ./server/tsconfig.api.json
$ node ./dist/api/bin/www
```
Navigate to `http://localhost:3000` and you should see the default Angular page again. Make a `GET` request to `http://localhost:3000/api/Oliver` and you should get `{"greeting" : "Hello, Oliver"}` as the response.

Shut the server down and proceed.

With Express serving both the API and the static browser files, we'll add some scripts to the `package.json` file to make all of these steps more seamless and so the server can listen to file changes.

First, we'll need to install the following npm tools globally:
 - `npm install --global nodemon` - Nodemon is a development utility that will restart our API server whenever changes to the code are detected.
 - `npm install --global concurrently` - Concurrently is a tool that can run multiple npm processes in the same terminal, and it provides several options to deal with any of the processes failing. We'll use concurrently to watch and rebuild the client and server side code at the same time.

Now add the following scripts to `package.json`:
```javascript
{
    // ...
    "scripts": {
        //...
        "clean": "rm -rf ./dist/api && rm -rf ./dist/public/",
        "cp:www": "mkdir -p ./dist/api/bin && cp ./server/bin/www ./dist/api/bin/",
        "dev": "concurrently -k \"tsc -p ./server/tsconfig.api.json -w\" \"cd ./dist/api && nodemon -r ./bin/www --watch\" \"ng build --watch\""
    }
}
```
Here's what they do:
 - `$ npm run clean` - will clean out the directories where the compiled output goes in case we need to make a fresh start.
 - `$ npm run cp:www` - This copies `./server/bin/www` to its proper location. 
 - `$ npm run dev` - Using Concurrently, we compile Typescript files every time there are changes, run the files in Node and watch for changes with Nodemon, then watch for changes to the Angular files and build those accordingly.

 Run each of those scripts in order and you should get the same results as above when making requests to `http://localhost:3000`.

## Bonus: Refresh the browser when client-side code changes
Unfortunately, one of the tradeoffs to using the above scripts instead of Angular's `ng serve` is that we'd have to manually refresh the browser each time we make changes. Configuring Express with a couple of npm packages - `livereload` and `connect-livereload` - can accomplish this in our current setup.

Install the packages as development dependencies:
```bash
$ npm install --save-dev livereload connect-livereload
```

In `app.ts`, import the libraries:
```typescript
import * as livereload from 'livereload';
import * as connectLivereload from 'connect-livereload';
```
And underneath the line where the client directory is declared, paste the following:
```typescript
  const app = express();
  const clientDir = path.join(__dirname, '../public');

  // In development, refresh Angular on save just like ng serve does
  let livereloadServer: any;
  if (process.env.NODE_ENV !== 'production') {
      livereloadServer = livereload.createServer();
      livereloadServer.watch(clientDir);
      app.use(connectLivereload());
      livereloadServer.once('connection', () => {
        setTimeout(() => livereloadServer.refresh('/'), 100);
      });
  }
```
In the code, we're creating a `livereload` server and setting it to listen to changes to the client directory. Then, `connect-livereload` provides middleware to the Express app that injects a bit of temporary code in our static files that makes the browser aware of any changes and refreshes accordingly.

Lastly, if your linter is giving you grief about not having type declarations for `livereload` and `connect-livereload`, you can add a type declarations file in the server directory:
```bash
$ touch ./server/decs.d.ts
```
And paste the following:
```typescript
declare module 'livereload';
declare module 'connect-livereload';
```

I got this configuration mainly from [this article](https://bytearcher.com/articles/refresh-changes-browser-express-livereload-nodemon/) which goes into much more detail about what is actually happening. 

## Putting it all together
As one final proof-of-life, let's get our Angular application to talk to the Express back end.

In the Angular app, open `app.module.ts` and paste the all of the following:
```typescript
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
```
The only difference is that we've added the `FormsModule` and the `HttpClientModule`.

Next open `app.component.ts` and replace the entire file with:
```typescript
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-root',
  template: `
    <div class="app-container" style="width:20rem; margin: 2rem auto;">
      <div class="form-group" >
        <label for="name-input">Enter a name:</label>
        <input class="form-control" id="name-input" required [(ngModel)]="nameInput">
        <button class="btn btn-primary"(click)="greetMe()">Greet Me</button>
      </div>
      <div class="name-display">
        <p *ngIf="responseDisplay && responseDisplay.length > 0">
          {{ responseDisplay }}
        </p>
      </div>
    </div>
  `
})
export class AppComponent {

  constructor(private http: HttpClient) {  }

  nameInput: string = '';
  responseDisplay: string = '';

  greetMe(): void {
    this.http.get(`/api/${ this.nameInput }`)
      .subscribe((response: any) => this.responseDisplay = response.greeting);
  }
}
```
Optionally, you can add some basic Bootstrap so the result isn't hideous. In `styles.css`, add:
```css
/* You can add global styles to this file, and also import other style files */
@import url('https://unpkg.com/bootstrap@3.3.7/dist/css/bootstrap.min.css');
```
Run the entire application again with `$ npm run dev`, and you should see a tiny form where you can send a name to the server, then get a greeting back.

![Chrome result](https://dev-to-uploads.s3.amazonaws.com/i/eglb282t98fzsdquhwyy.jpg)

## Conclusion
The above steps should provide a good start to building out a fullstack application entirely in Typescript. By starting with Angular, we're bringing in a rich set of tools and configurations, then adding Express.js and some scripts for a convenient and automated development workflow. 

In an upcoming tutorial, I'll show how to add in MongoDb (the __M__ in __MEAN__) and how to use Docker to reduce some of the overhead in setting up a local database.