24

I want to serve static HTML files which are in a /dist folder outside of the Nest project. index.html is loaded successfully but it fails to load any JS file (404 error).

I have a Node/Express.js project which uses

app.use('/', express.static('../client/dist'))

and it works perfectly fine.

In the Nest project, however,

app.setBaseViewsDir(join(__dirname, '../../client/dist'))

does not do the trick.

In the AppController I tried

import { Response } from 'express';

@Get()
  get(@Res() res: Response) {
    res.sendFile('index.html', {
      root: '../client/dist',
    });
  }

But no luck.

As mentioned, the index.html is loaded successfully. So the problem is not a wrong path. Neither is the problem wrong src-paths in the index.html because in the Express project the exact same files are used.

/dist
  |-index.html
  |-main.js
  |-etc.

In the index.html:

<script type="text/javascript" src="main.js"></script>

It does not work either when I put the dist folder into the Nest project (and adapt the path).

I found the solution:

I use the express module now:

import * as express from 'express';
...
app.use('/', express.static('../client/dist'));
Kim Kern
  • 54,283
  • 17
  • 197
  • 195
Maurice Wipf
  • 647
  • 2
  • 7
  • 13
  • Still open questions? :-) – Kim Kern Mar 30 '19 at 14:51
  • @KimKern No, thank you. I was able to solve it with the solution mentioned above :-) – Maurice Wipf Apr 01 '19 at 19:50
  • 2
    Glad to hear you solved it. :-) In general, consider adding your solution as an answer instead of updating your question so others can profit from it. Then you can accept your answer to show others your problem was solved. In this case, I'd suggest using the nest API instead of the native express api though, see my solution. – Kim Kern Apr 02 '19 at 13:48
  • For Angular + NestJS + Cloud Functions for Firebase see the answer: https://stackoverflow.com/a/57493387/11127383 – Daniel Danielecki Aug 14 '19 at 11:00
  • +1 for moving your found solution into an own answer! Don't know why, but useStaticAssets did not work for me but your solution did. And I only found your solution after reading all answers twice. – andymel Mar 28 '23 at 18:58

5 Answers5

19

For serving static files you have to use useStaticAssets() instead of setBaseViewsDir():

app.useStaticAssets(join(__dirname, '../../client/dist'))

When you use useStaticAssets you don't need to set up a controller, all your files will be served automatically:

Let's say under client/dist you have the files index.html and pic.jpg. They will be served as:

localhost:3000 -> index.html
localhost:3000/pic.jpg -> pic.jpg

Setting the base views dir is needed when you want to use a view engine like for example hbs, see docs.

Kim Kern
  • 54,283
  • 17
  • 197
  • 195
17

Regarding the official documentation of Nest.js one should serve static files like this:

Install the required package:

npm install --save @nestjs/serve-static

Update app.module.ts to look like this:

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ServeStaticModule } from '@nestjs/serve-static';
import { join } from 'path';

@Module({
  imports: [
    ServeStaticModule.forRoot({
      rootPath: join(__dirname, '..', 'client'),   // <-- path to the static files
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
Philipp Kief
  • 8,172
  • 5
  • 33
  • 43
  • I used it as described. But the browser still targets the backend. Is there any information, which tells me, if the serveStatic works? – Makuna Nov 29 '22 at 13:39
8

If you have something like this

/public
  |-index.html
  |-main.js
  |-etc.
/src
  |-app.controller.js
  |-app.module.js
  |-main.js

In main.ts or main.js

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useStaticAssets(join(__dirname, '..', 'public'));
  app.setViewEngine('html');

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

In app.module.js

@Module({
  imports: 
  [ 
    ServeStaticModule.forRoot({
      rootPath: join(__dirname, '..', 'public'),
      exclude: ['/api*'],
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

In app.controller.js

@Controller()
@Dependencies(AppService)
export class AppController {
  constructor(appService) {
    this.appService = appService;
  }

  @Get()
  @Render('index')
  root() {
  }
}

With this code, you can do the trick :) :) :)

Marco Muñoz
  • 181
  • 2
  • 3
3

If you decide to do it in "main.ts" or "app.module.ts" (you don't need them both), it's better for you to add "prefix" option in the "main.ts":

app.useStaticAssets(join(__dirname, '..', 'public'), {prefix: '/public'});

Or the "serveRoot" option in the "app.module.ts":

ServeStaticModule.forRoot({
   serveRoot: '/public',
   rootPath: join(__dirname, '..', 'public'),
}),

And make the link to your static files as "[your host]/public/[your dir and files]" To divide your static path than other paths.

Israel
  • 1,165
  • 11
  • 11
2

Write app.useStaticAssets(join(__dirname, '../../client/dist')) in main.ts

And also you can try for fastify app this:

import { resolve } from 'path';

app.useStaticAssets({
    root: resolve("./build")
});
Philipp Kief
  • 8,172
  • 5
  • 33
  • 43
Serhii
  • 67
  • 6
  • 2
    That's incorrect. When you use `useStaticAssets` you don't need any controller. The files will be served automatically, `index.html` will be accessible under the root path. – Kim Kern Mar 27 '19 at 11:09