Hosting an Angular application with express

Yannick Haas
medialesson
Published in
3 min readApr 2, 2024

--

This is part of a multi-post series detailing how to consume environment variables in an Angular application, replacing the traditional environment.ts files. The other posts can be found here:

In my last post we took a look at how to share interfaces between Angular and Node. We created two applications: angular-environment-variables and runtime-host . The name of the Node project isn’t just random. We’re actually going to use the Runtime Host to serve our Angular application.

The Runtime Host has three main responsibilities:

  1. Host the Angular application (client)
  2. Read environment variables
  3. Provide an endpoint which returns the environment variables through the interface AppConfiguration

Reading environment variables and hosting an endpoint

To read environment variables we use process.env in the Runtime Host, which results in:

// main.ts
// Put this after const app = ...
const appConfig: AppConfiguration = {
// If the variable is not set, we'll assume our application should run in production mode
angularEnvironment: process.env['ANGULAR_ENVIRONMENT'] === 'DEVELOPMENT' ? 'development' : 'production',
apiUrl: process.env['API_URL'] ?? null,
backgroundColor: process.env['BACKGROUND_COLOR'] ?? null
}

Now we’ve got our configuration, we just need to define an endpoint for it.

// Provide configuration on /config
app.get('/config', (_request: Request, res: Response) => {
res.json(appConfig);
});

If we were to start the app now, we wouldn’t be able to get the config as express doesn’t yet listen for requests. To actually get express to listen to requests we have to tell it to listen on a specific port:

app.listen(port, () => {
console.log(`Server listening on port: ${port}`);
});

If you start the server now and query http://localhost:3333/config you should get the following response

{
"angularEnvironment": "production",
"apiUrl": null,
"backgroundColor": null
}

Serving the Angular application

To serve the Angular application with express, we’re going to use express’s feature to serve static files. The code (or any app.use) should come before any app.get or similar.

// Uses the static content located in ./client
app.use(express.static(`${__dirname}/client/`));

__dirname resolves to the path the main.js of our Runtime Host is located, so our client’s files have to be in the sub-folder client . When we’re serving the Runtime Host locally, we of course don’t have such a folder, but express doesn’t throw an error, if it can’t locate it, so we don’t have to use any try catch or similar.

To redirect any requests not going to /config or any other defined endpoint, we have to implement a “catch all” after all our defined endpoints. Otherwise we’d just get a 404.

// ...
// app.get('/config' ...

// Other requests should be redirected to our Angular client
app.use('*', (_req: Request, res: Response) => {
res.sendFile(`${__dirname}/client/index.html`);
});

Only requests going to /config will be handled by the Runtime Host.

The main.ts of the Runtime Host should now look like this:

import express, { Request, Response } from 'express';
import { AppConfiguration } from '@angular-environment-variables/app-configuration';

const port = process.env.PORT ? Number(process.env.PORT) : 3333;

const app = express();

const config: AppConfiguration = {
angularEnvironment: process.env['ANGULAR_ENVIRONMENT'] === 'development' ? 'development' : 'production',
apiUrl: process.env['API_URL'] ?? null,
backgroundColor: process.env['BACKGROUND_COLOR'] ?? null
}

// If not found, doesn't throw an error.
app.use(express.static(`${__dirname}/client/`, {}));

// Provide configuration on /config
app.get('/config', (_request: Request, res: Response) => {
res.json(config);
});

// Other requests should be redirected to our Angular client
app.use('*', (_req: Request, res: Response) => {
res.sendFile(`${__dirname}/client/index.html`);
});

app.listen(port, () => {
console.log(`Server listening on port: ${port}`);
});

If you compile your apps now and copy the client’s file to the Runtime Host’s client folder, you should be able to access it, once you start the node application.

--

--