2
votes

I have a NestJS api that I want to use to also serve a frontend, from Angular. This is deployed onto heroku. By backend routes use the prefix '/api' and I want all other requests to respond with the index.html from my build Angular app.

My folder structure for the deployed app is:

-dist
  -client
    -index.html
    -.js files
  -server
    -main.js
    -other dirs/modules

I have tried all kinds of things so far and they have all worked locally. The problem is deploying to heroku. The closest I've gotten (with help from this article: https://medium.com/@bo.vandersteene/use-nest-as-your-server-side-application-with-an-angular-frontend-540b0365bfa3) is creating a frontend middleware in the NestJS app that looks at the request path and file extensions, then uses res.sendFile and path.resolve to create the filepath to respond with.

import { Injectable, NestMiddleware } from '@nestjs/common';
import { apiPrefix } from '../../config';
import { Request, Response } from 'express';
import { resolve } from 'path';

const allowedExtentions = [
  '.js',
  '.ico',
  '.css',
  '.png',
  '.jpg',
  '.woff2',
  '.woff',
  '.ttf',
  '.svg',
];

const prefix = apiPrefix;

@Injectable()
export class FrontendMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: () => void) {
    const { baseUrl } = req;
    if (baseUrl.indexOf(prefix) === 0) {
      next();
    } else if (
      allowedExtentions.filter(extention => baseUrl.indexOf(extention) > 0)
        .length > 0
    ) {
      res.sendFile(resolve(`./dist/client/${baseUrl}`));
    } else {
      res.sendFile(resolve('./dist/client/index.html'));
    }
  }
}

This works LOCALLY. When deploying to heroku, I get an error: [ExceptionsHandler] ENOENT: no such file or directory, stat '/app/dist/client/index.html' This doesn't make sense, though, because when logging into the Heroku bash for my project, I can see the exact file path and file. The file exists but for some reason, NestJS does not recognize it.

Any help would be appreciated.

1
This is most likely because of heroku deployment. You need to use static.json. Here is a related SO thread stackoverflow.com/questions/50504161/…eenagy

1 Answers

3
votes

What I've done with great success is in your main.ts file, add app.useStaticAssets('your/path/to/dist/client') and then at your GET / route you serve the index.html file. e.g.


@Injectable()
export class AppController {
  @Get()
  sendApplication(@Res() res) {
    res.sendFile('index.html');
  }
}

I should mention that this requires your angular app to be built using ng build --prod in the Heroku build stage or before the code makes it to Heroku.