How to actually integrate Angular and NestJS

I don’t know who needs to hear this. But your frontend and backend systems don’t need to be completely separate.

I started anew side project recently. You know, one of things that allows me to tinker with new technology but will probably never be finished.

I’m using Angular for the frontend and nestjs for the backend. All good. But then I go to do something that I thought was very normal and common and run into a wall.

I want to integrate the two frameworks. I want to serve my initial html with nestjs and add script tags so that Angular takes over the frontend. This will allow me to do dynamic things on the backend and frontend however I want. But also deploy the system all as one cohesive product. Apparently this is no longer How Things Are Done.

I literally could not find documentation on how to do this. When you read the docs and blog posts, everybody expects you to just have two systems that run entirely independently. Here’s the server for your backend and here’s the entirely different server for your frontend. Hashtag winning!

When I google for “integrate angular and nestjs”, nobody knows what I’m talking about. On the surface, this seems like is a great technical blog post from LogRocket. It says “I will teach you how. First, set up two separate servers…”

I think I know why the community has ended up in this place. But that’s a rant for another blog post. Let me try to explain what I’m talking about.

Angular is designed as an a frontend framework (let’s set aside SSR for now). The primary output of an Angular build is javascript and css files that are meant to run in the browser. When you run ng build, you’ll get a set of files put into your output folder. Usually the folder is dist/<your_project_name>. Let’s look at what’s in there.

polotek $> ls -la dist/my-angular-project
-rw-r--r--  1 polotek  staff    12K Sep 13 14:15 3rdpartylicenses.txt
-rw-r--r--  1 polotek  staff   948B Sep 13 14:15 favicon.ico
-rw-r--r--  1 polotek  staff   573B Sep 13 14:15 index.html
-rw-r--r--  1 polotek  staff   181K Sep 13 14:15 main.c01cba7b28b56cb8.js
-rw-r--r--  1 polotek  staff    33K Sep 13 14:15 polyfills.2f491a303e062d57.js
-rw-r--r--  1 polotek  staff   902B Sep 13 14:15 runtime.0b9744f158e85515.js
-rw-r--r--  1 polotek  staff     0B Sep 13 14:15 styles.ef46db3751d8e999.css

Some javascript and css files. Just as expected. A favicon. Sure, why not. Something about 3rd party licenses. I have no idea what that is, so let’s ignore it. But there’s also an index.html file. This is where the magic is. This file sets up your html so it can serve Angular files. It’s very simple and looks like this.

<!doctype html>
<html lang="en" data-critters-container>
<head>
    <meta charset="utf-8">
    <title>MyAngularProject</title>
    <base href="/">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="icon" type="image/x-icon" href="favicon.ico">
    <link rel="stylesheet" href="styles.ef46db3751d8e999.css">
</head>
<body>
    <app-root></app-root>
    <script src="runtime.b3cecf81bdcc5839.js" type="module"></script>
    <script src="polyfills.41808b7aa9da5ebc.js" type="module"></script>
    <script src="main.cf1267740c62d53b.js" type="module"></script>
</body>

</html>

It turns out the web browser still works the way it always did. You use <script> tags and <link> tags to load your javascript and css into the page. But we want to let the backend do this rather than using this static html file.

I’m using NestJS for the backend. It’s modeled after Angular, so a lot of the structures are very similar. Just without all of the browser-specific stuff. Nest is not so important here though. This problem is the same with whatever backend you’re using. The important thing is how static files are served. If you copy the above html into a backend template, it probably won’t work. This is what you get in the browser when you try this with NestJS.

Screenshot of web browser. The page is blank and the Chrome devtools are open to the Network tab. You can see that several javascript and css files failed to load with a 404 error code.

This is part of my gripe. By default, these are two separate systems right now. So NestJS doesn’t know that these files exist. And they’re in two separate folders. So it’s unclear what the best way is to integrate them. In the future, I might talk about more sustainable ways to do this for a real project. But for now, I’m going to do the simple thing just to illustrate how this is supposed to work.

In NestJS, or whatever backend you’re using, you should be able to configure where your static files go. In Nest, it looks something like this.

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);

  app.useStaticAssets(path.resolve("./public"));

  await app.listen(3000);
}
bootstrap();

So there should be a folder called public in your backend project, and that’s where it expect to find javascript and css files. So here’s the magic. Copy the Angular files into that folder. Let’s say you have the two projects side by side. It might look like this.

polotek $> cp my-angular-project/dist/my-angular-project-ui/* my-nest-project/public/

This will also copy the original index.html file and the other junk. We don’t care about that for now. This is just for illustration. So now we’ve made NestJS aware of our Angular files. Reload your NestJS page and you should see this.

Screenshot of web browser. The page shows the standard "Welcome" screen for new Angular projects.The Chrome devtools are open to the Network tab. You can see all files loading properly.

We did it! This is how to integrate a cohesive system with frontend and backend. The frontend ecosystem has wandered away from this path. But this is how the web is supposed to work in my opinion. And more importantly, it is actually how a lot of real products companies want to manage their system.

I want to acknowledge that there are still a lot of unanswered questions here. You can’t deploy this to production. The purpose of this blog post is to help the next person like me who was trying to google how to actually integrate Angular and a backend like NestJS because I assumed there was a common and documented path to doing so. If this was useful for you, and you’re interested in having me write about the rest of what we’re missing in modern frontend, let me know.