スキップしてメイン コンテンツに移動

[Nest.js] Show static files

Intro

I wanted to use Nest.js and WebRTC(node-webrtc).
NestJS - A progressive Node.js framework
Documentation | NestJS - A progressive Node.js framework

And because I wanted to try with simple page(not use JavaScript frameworks), I added static HTML, CSS, JavaScript into a Nest.js project.

Prepare

Install

First, I installed @nestjs/cli.
First steps | NestJS - A progressive Node.js framework

As same as last time, I couldn't do global install because I had used Volta.
But I could installed by volta.
volta install @nestjs/cli

Create project

nest new nest-web-rtc-sample
volta pin node@12

Run

npm start
After doing "npm start", I could getting "Hello World!" from http://localhost:3000.

Add static files

I could add static files by two ways.

@nestjs/serve-static

First one of them was using "serve-static".
Serve Static | NestJS - A progressive Node.js framework

npm install --save @nestjs/serve-static

And I needed adding a module into app.module.ts

app.module.ts


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'),
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Add static files

And I added static files.
L nest-web-rtc-sample
    L client
        L css
            L style.css
        L img
            L car.png
        L js
            L main.bundle.js
        L node_modules
        L ts
            L main.ts
            L rtc-sample.ts
        L index.html
        L hello.html
        L package.json
        L package-lock.json
        L tsconfig.json
        L webpack.config.js
    L {Nest.js project files}

style.css


#main-title{
    font-weight: bold;
    color: blue;
}

main.ts


import { RtcSample } from "./rtc-sample";

function init(){
    const sample = new RtcSample();
    sample.getMessage();
}
init();

rtc-sample.ts


export class RtcSample{
    public getMessage() {
        return fetch('/', {
            method: "GET",
            mode: "cors"
        })
        .then(res => res.text())
        .then(v => console.log(v));
    }
}

index.html


<!DOCTYPE html>
<html>
    <head>
        <title>Index page</title>
        <script src="/js/main.bundle.js"></script>
        <link rel="stylesheet" type="text/css" href="/css/style.css" />
    </head>
    <body>
        <div id="main-title">Index page</div>
        <img src="/img/car.png">
    </body>
</html>

hello.html


<!DOCTYPE html>
<html>
    <head>
        <title>Hello World</title>
        <script src="/js/main.bundle.js"></script>
        <link rel="stylesheet" type="text/css" href="/css/style.css" />
    </head>
    <body>
        <div id="main-title">Hello World!</div>
        <img src="/img/car.png">
    </body>
</html>

pacakage.json


{
    "dependencies": {
        "ts-loader": "^7.0.1",
        "tsc": "^1.20150623.0",
        "typescript": "^3.8.3",
        "webpack": "^4.43.0",
        "webpack-cli": "^3.3.11"
    }
}

webpack.config.js


var path = require('path');

module.exports = {
    mode: 'development',
    entry: {
        'main': './ts/main.ts',
    },
    module: {
        rules: [
            {
                test: /\.tsx?$/,
                use: 'ts-loader',
                exclude: /node_modules/
            }
        ]
    },
    resolve: {
        extensions: ['.tsx', '.ts', '.js']
    },
    output: {
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, './js'),
        library: 'Page',
        libraryTarget: 'umd'
    }
}

Exclude client files from tsconfig.json of Nest.js

I had to exclude TypeScript files of client from tsconfig.json of Nest.js, or they were also transpiled by building the Nest.js project.

tsconfig.json


{
  "compilerOptions": {
    "module": "commonjs",
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "target": "es2017",
    "sourceMap": true,
    "outDir": "./dist",
    "baseUrl": "./",
    "incremental": true
  },
  "exclude": ["node_modules", "dist", "client"]
}

tsconfig.build.json


{
  "extends": "./tsconfig.json",
  "exclude": ["node_modules", "test", "dist", "**/*spec.ts", "client"]
}


Access index.html

I could see index.html when I access http://localhost:3000/client.
Only "index.html" were able to be shown.
If there was no files what was named "index.html", an exception would be thrown.

Other html files?

I couldn't use other html files(ex. hello.html)?

No. I could access hello.html by http://localhost:3000/hello.html.
Of cource, I could access index.html by http://localhost:3000/index.html.

To tell the truth, I could access index.html in all paths under the http://localhost:3000 except the paths what were routed by controller or static files.


useStaticAssets

Another one was "useStaticAssets".
MVC | NestJS - A progressive Node.js framework

This way didn't require any other packages.
But I needed changing main.ts of Nest.js.

main.ts


import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { NestExpressApplication } from '@nestjs/platform-express';
import { join } from 'path';

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(
    AppModule,
  );
  app.useStaticAssets(join(__dirname, '..', 'client'));
  await app.listen(3000);
}
bootstrap();


And I added static files as same as using "serve-static".

According to the document, if I used template engine such as hbs, I should use this way.

Access static files

I could access index.html by http://localhost:3000/index.html.
And the index.html was also routed root URL.

So when I used "useStaticAssets", I couldn't get "Hello World!" from http://localhost:3000.

sendFile

I also could publish files by "sendFile".

app.controller.ts


import { Controller, Get, Res } from '@nestjs/common';
import { AppService } from './app.service';
import { join } from 'path';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}
...
  @Get('/api/file')
  getFile(@Res() res) {
    return res.sendFile(join(__dirname, '..', 'client/hello.html'));
  }
}


When I wanted to pass the controller to publish HTML, I should choose this.

__dirname was changed?

When I use "serve-static", after I restarted the project some times, __dirname values of app.module.ts was changed and I couldn't access static files.

After I set "incremental" of tsconfig.json false and delete "dist" folder, and restarted, I could access again.

Though, there might be other better way.

コメント

このブログの人気の投稿

[Angular][ASP.NET Core] Upload chunked files

Intro I wanted to send files to Web application (made by ASP.NET Core). If the file size had been small, I didn't need do any special things. But when I tried to send a large file, the error was occurred by ASP.NET Core's limitation. Though I could change the settings, but I didn't want to do that, because I hadn't known the file sizes what would been actually using. So I splitted the data into chunks first, and sent them. After receiving all chunks, I merged them into one file. There might be some libraries or APIs (ex. Stream API) what did them automatically, but I couldn't find them. What I did [ASP.NET Core] Make CORS enabled [Angular] Split a large file into chunks [Angular][ASP.NET Core] Send and receive data as form data [ASP.NET Core] Merge chunks into one file [ASP.NET Core] Make CORS enabled Because the client side application(Angular) and the server side application(ASP.NET Core) had been separated, I had to make CORS(Cross-Origin Requests) ...

[Angular] Sending file with Observable and showing loading screen

Intro When I tried sending file data on last time, I had confused with "promise.then", "async/await" and "Observable". [Angular][ASP.NET Core] Upload chunked files So I wanted to distinct them, and this time, I tried to use "Observable" because HttpClient return Observable<any>. Call observables in order I sended file in these steps. Read file by FileReader Create directory for saving chunks send and saving chunks merge chunks to one file and delete chunks Each steps used the former steps result. So I could write by Promise.then like below. this.executeStep1() // return Promise<UploadResult> .then(result => this.executeStep2(result)) // return Promise<UploadResult> .then(result => this.executeStep3(result)) // return Promise<UploadResult> .catch(reason => console.log(reason)); Result I could write with pipe & flatMap. file-uploader.service.ts public upload(file: File): Observable<U...