Hosting an Angular application with express
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:
- Host the Angular application (client)
- Read environment variables
- 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.