Maurice Wipf
Maurice Wipf

Reputation: 707

How to serve static HTML files in Nest.js?

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'));

Upvotes: 26

Views: 68360

Answers (7)

Chris Barr
Chris Barr

Reputation: 34125

I had a lot of trial an error with the solutions here.

My file structure has a folder public right next to src with the NestJS app source.

This is what ended up working for me

import { INestApplication } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { resolve } from 'path';

import { AppModule } from './app.module';

async function bootstrap(): Promise<void> {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);
  //                                   ^^^ This generic type is key!

  app.useStaticAssets(resolve('./public/assets'), { prefix: '/assets' });

  await app.listen(3000);
}

when I visit localhost:3000/assets/example.png I get the file at /public/assets/example.png

Upvotes: 0

Shubham Verma
Shubham Verma

Reputation: 9961

I was facing the same issue, I want to serve the public images through API, so I tried the below approach:

Create a constant to configure the public folder:

// src\constants\constant.ts
export const PUBLIC_IMAGES = 'public/images';

Add ServeStaticModule in your app.module.ts:

// src\app.module.ts
import { Module } from '@nestjs/common';
import { ServeStaticModule } from '@nestjs/serve-static';
import { join } from 'path';
import { PUBLIC_IMAGES } from './constants/constant';

@Module({
 imports: [
  ServeStaticModule.forRoot({
   serveRoot: '/public',
   rootPath: join(__dirname, '..', PUBLIC_IMAGES),
  })
 ],
 controllers: [],
 providers: [ ],
})


export class AppModule {}

Create a public folder for your images:

root->public\images\myImage.png

Hit URL in the browser:

http://localhost:3010/public/myImage.png

Upvotes: 0

Israel
Israel

Reputation: 1454

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.

Upvotes: 3

Marco Mu&#241;oz
Marco Mu&#241;oz

Reputation: 181

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 :) :) :)

Upvotes: 8

Philipp Kief
Philipp Kief

Reputation: 8613

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 {}

Upvotes: 20

Serhii
Serhii

Reputation: 67

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")
});

Upvotes: 2

Kim Kern
Kim Kern

Reputation: 60537

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.

Upvotes: 19

Related Questions