The Angular team continues to recommend Monorepositories as a solution to many common problems that developers encounter building software at scale.
More than once now, I've needed to build an API layer to give my Angular application access to backend processing, data, and functionality. You can do a lot of this with Firebase functions, but occasionally you want to take control of the full stack by having your own server.
I've filmed a video version of this content as well:

I know not everyone will agree with my choices, but hopefully this serves as both a starting point for developers looking to get started with this technique, and as an example for those looking to further adopt TypeScript into their backend architecture.

NodeJS with TypeScript

There are lots of great ways of configuring technologies like NodeJS, Express, etc, but I wanted to do this in a way that met two important criteria:
  • My server must live inside of my Angular application directory and in the same git repository
  • My server must be written in and run with TypeScript

The Process

The process I recommend has 6 steps:
  1. Create a server folder
  2. Give it its own dependencies
  3. Give it its own tsconfig
  4. Create an Express server with TypeScript
  5. Setup some routes
  6. Integrate my routes into my Angular app

  1. Add a folder for your server

I like to create a folder right at the root level called /server. This is almost entirely a preference though. I like to treat my server as separately as possible from my client-side code. If you have a relatively complex Angular project with a library or two, you might be using /projects. You could totally put it there if you prefer as well.

  1. Create a package.json so you can add dependencies

Create a package.json for this folder so that it can have its own dependencies independently from the Angular application.
cd server
yarn init

Now add the dependencies the app will need to run on the command line.
yarn add ts-node ts-node-dev tslint typescript express @types/express concurrently

  1. Create a tsconfig.json file

We want a typescript configuration that is independent from the Angular one, so we'll create a new file with:
yarn global add typescript
tsc --init

At this point, feel free to play with any TypeScript flags you might want to change (like strict).

  1. Create an Express server with TypeScript

Next up, we're going to create a server.ts file with a very simple express setup.
server.ts
// equivalent of older: const express = require('express')
import * as express from 'express';

const app = express();

// Allow any method from any host and log requests
app.use((req, res, next) => {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
    res.header('Access-Control-Allow-Methods', 'OPTIONS, GET, POST, PUT, DELETE');
    if('OPTIONS' === req.method) {
        res.sendStatus(200);
    } else {
        console.log(`${req.ip} ${req.method} ${req.url}`);
        next();
    }
}

// Handle POST requests that come in formatted as JSON
app.use(express.json())

// A default hello word route
app.get('/', (req, res) => {
    res.send({hello: 'world'});
});

// start our server on port 4201
app.listen(4201, '127.0.0.1', function() {
    console.log("Server now listening on 4201");
});

I like to wire up my server directly into my package.json's serve script with the following snippet.
"scripts": {
    ...
    "server": concurrently \"ng serve\" \"server/node_modules/.bin/ts-node-dev server/server.ts\"
}

By using concurrently, whenever I run yarn serve I'll get both the backend and front-end spun up and live reloading whenever I make changes to my application. Hurray for having one command to run everything!

  1. Setup some routes

Now we can remove the hardcoded app.get() that we put in our server.ts file and we can create a nested structure of routes. Doing this will keep your server.ts manageable and small, giving you more maintainable code.
To create a route configuration file, I'll make a new routes.ts file that will export a router that the main server will use. You can repeat this strategy as many times as you like , creating sub-APIs to any granularity that you would like.
routes.ts
import * as express from 'express';
export const routes = express.Router();

routes.get('/', (req, res) => res.send({hello: 'world'});
routes.get('/users', (req, res) => res.send([]);
routes.post('/users', (req, res) => res.send({body: req.body});

server.ts - replacing the app.get() hello world call
// Put at the top
import { routes } from './router'; 

// Put after the express.json() call
app.use('/', routes); 

  1. Integrate my routes into my Angular app

Now you can use the standard HttpClient service from Angular to make requests to our server. For example, if you make a post request to http://localhost:4201/users/ and it should be echoed back to us.
Now it's time to build out your server with APIs that will actually be useful to you (I'll cover connecting to a DB with this setup in the future), and consuming those APIs from your Angular app.