简体   繁体   English

将 Nest.js 应用程序导入为简单的 Express 中间件

[英]Import a Nest.js app as a simple Express middleware

I've a Nestjs app (a Rest API) that I would like to import in another node module, as a simple Express middleware (not a Nest middleware).我有一个 Nestjs 应用程序(一个 Rest API),我想将它作为一个简单的 Express 中间件(不是 Nest 中间件)导入另一个节点模块。 Actually I'm still not able to make it working.实际上,我仍然无法使其正常工作。

// main.ts  
// => The main file of my Nest app, this one is working properly.

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}
bootstrap();

// app.middleware.ts

import {Injectable, NestMiddleware} from '@nestjs/common';
import {NestFactory} from '@nestjs/core';
import {AppModule} from './app.module';
import {ExpressAdapter} from '@nestjs/platform-express';
import express, {Request, Response} from 'express';

const bootstrap = async () => {
  const expressApp = express();
  const adapter = new ExpressAdapter(expressApp);
  const app = await NestFactory.create(AppModule, adapter);
  await app.init();
  return app;
};

@Injectable()
export class AppMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: Function) {
    return bootstrap();
  }
}
// express-app.ts  
// => Here I'm trying to load my app through a simple Express middleware, but it doesn't works.

import express from 'express';
import { AppMiddleware } from './app.middleware';

const app = express();
const PORT = process.env.PORT || 3000;

app.use((req, res, next) => {
  const app = new AppMiddleware().use(req, res, next);
  app.then(next);
});

app.listen(PORT, () => {
  console.log(`app running on port ${PORT}`);
});

When running my app from main.ts it's working properly (all the routes are working and I'm getting the correct data).main.ts运行我的应用程序时,它工作正常(所有路线都在工作,我得到了正确的数据)。 However when I try to run the app through express-app.ts , all the routes seems working (they are displayed in the terminal), but instead of returning a JSON object, in any case I'm getting this error:但是,当我尝试通过express-app.ts运行应用程序时,所有路由似乎都在工作(它们显示在终端中),但不是返回 JSON 对象,无论如何我都会收到此错误:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <title>Error</title>
</head>

<body>
    <pre>[object Object]</pre>
</body>

</html>

Nest component versions:嵌套组件版本:

- @nestjs/common: "^6.10.14"
- @nestjs/core: "^6.10.14"
- @nestjs/platform-express: "^6.10.14"
- express: "^4.16.4"

While I don't condone the use of Nest as a middleware itself, it is possible.虽然我不容忍将 Nest 用作中间件本身,但这是可能的。 Using a basic set up from a nest new express-server -p npm to create the new NestJS application, and setting up a small express server with src/server.ts I was able to get the following code working.使用nest new express-server -p npm的基本设置来创建新的 NestJS 应用程序,并使用src/server.ts设置一个小型 express 服务器,我能够使以下代码工作。

app.middleware.ts app.middleware.ts

import { Injectable, NestMiddleware } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { ExpressAdapter } from '@nestjs/platform-express';
import { AppModule } from './app.module';

const bootstrap = async (express: Express.Application) => {
  const app = await NestFactory.create(AppModule, new ExpressAdapter(express));
  await app.init();
  return app;
}

@Injectable()
export class AppMiddleware implements NestMiddleware {

  constructor(private expressInstance: Express.Application) {}

  use(req: any, res: any, next: () => void) {
    console.log('In Nest middleware');
    return bootstrap(this.expressInstance);
  }
}

app.controller.ts app.controller.ts

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

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
}

app.service.ts应用服务.ts

import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello World!';
  }
}

app.module.ts app.module.ts

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

server.ts服务器.ts

import * as express from 'express';

import { AppMiddleware } from './app.middleware';

const app = express();

app.use((req, res, next) => {
  const nest = new AppMiddleware(app).use(req, res, next);
  nest.then(() => {
    next();
  }).catch(err => {
    console.log(JSON.stringify(err));
    next();
  });
});

app.listen(3000, () => {
  console.log('Listening on port 3000');
});

Build command构建命令

npm run build
# mapped to nest build

Start command启动命令

node dist/server.js

Test command测试命令

▶ curl http://localhost:3000
Hello World!

Console Log控制台日志

Listening on port 3000
In Nest middleware
[Nest] 24235   - 02/18/2020, 8:05:44 PM   [NestFactory] Starting Nest application...
[Nest] 24235   - 02/18/2020, 8:05:44 PM   [InstanceLoader] AppModule dependencies initialized +15ms
[Nest] 24235   - 02/18/2020, 8:05:44 PM   [RoutesResolver] AppController {/}: +3ms
[Nest] 24235   - 02/18/2020, 8:05:44 PM   [RouterExplorer] Mapped {/, GET} route +2ms
[Nest] 24235   - 02/18/2020, 8:05:44 PM   [NestApplication] Nest application successfully started +2ms

Keep in mind a few things:请记住以下几点:

1) with this approach, unless you cache your Nest server, you will build a new Nest server on each request, which will only slow your project down more as you grow with the Nest side of things. 1) 使用这种方法,除非您缓存您的 Nest 服务器,否则您针对每个请求构建一个新的 Nest 服务器,这只会随着您在 Nest 方面的发展而减慢您的项目速度。

2) You could instead pass your existing express server to the ExpressAdapter as you are partially doing in your existing code and start the server from the Nest app.listen() function instead. 2) 您可以改为将现有的 express 服务器传递给ExpressAdapter就像您在现有代码中所做的那样,而是从 Nest app.listen()函数启动服务器。 Just make sure to remove any error handling middleware as it will start to conflict with how Nest handles responses.只要确保删除任何错误处理中间件,因为它会开始与 Nest 处理响应的方式发生冲突。 You should instead move those functions to ExceptionFilters.您应该将这些函数移到 ExceptionFilters 中。

3) One of the errors in your app.middleware is that you are creating not only a new Nest instance on each call, but a new express instance too, which could really be confusing the node server. 3)您的app.middleware中的错误之一是您不仅在每次调用时都创建了一个新的 Nest 实例,而且还创建了一个新的 express 实例,这可能真的会混淆节点服务器。

4) The error that was coming in as [Object object] in case you were wondering, was a standard Express error Cannot GET / . 4) 如果您想知道,作为[Object object]出现的错误是标准的 Express 错误Cannot GET / Dunno why it was serialized strangely, but a JSON.stringify() in the catch helped resolve it.不知道为什么它被奇怪地序列化,但是捕获中的JSON.stringify()帮助解决了它。

Overall, I would not recommend this approach but it is possible to do.总的来说,我不推荐这种方法,可以这样做。

I know it's not exactly the answer to the question, but I'd just like to leave an example using this middleware.我知道这不完全是问题的答案,但我只想留下一个使用这个中间件的例子。

For my context, I thought it was okay to put everything in the nest, instead of putting the nest in the express.就我的情况而言,我认为把所有东西都放在巢里是可以的,而不是把巢放在快递里。 I needed to put all my standard express application, working with the node, with no special conditions, just join the 2, this was my scenario.我需要把我所有的标准快递应用程序,与节点一起工作,没有特殊条件,只需加入2,这就是我的场景。

I just took the global settings, like body-parser and dotenv and put it in my main file.我只是采用了全局设置,如 body-parser 和 dotenv,并将其放入我的主文件中。

src/main.ts src/main.ts

import dotenv from 'dotenv'
import bodyParser from 'body-parser'
import { useExpress } from './workspaces/poc/server'
import { TodoModule } from './workspaces/todo/todo.module'
import { NestFactory } from '@nestjs/core';

// was in src/workspaces/my-legacy-app/server.ts
dotenv.config()

async function bootstrap() {
  const app = await NestFactory.create(TodoModule);
  app.use(bodyParser.json());

  // was in src/workspaces/my-legacy-app/server.ts
  // also did not know how to resolve the issue of types, so use "any"
  useExpress(app.getHttpAdapter() as any)

  await app.listen(3000,() => {
    console.info(`App runnning on port: ${3000}`)
  });
}
bootstrap();

My old legacy main file我的旧遗留主文件

src/workspaces/legacy-app/server.ts src/workspaces/legacy-app/server.ts

import { validatorMiddleware } from './middlewares/validator.middleware'
import { logMiddleware } from './middlewares/log.middleware'
import { userRouter } from './routes/user.route'
import { Express } from 'express'

export function useExpress(server: Express){

  server.use(validatorMiddleware)
  server.use(logMiddleware)
  server.use('/user', userRouter)
  
  // commented because the server will go up here more, but just to show that it was the same way as in express
  // server.listen(
  //   process.env.PORT,
  //   () => console.log(`Server is running on port ${process.env.PORT ?? 3000}`)
  // )
}

Slightly different approach based on Jay McDoniel 's answer.基于Jay McDoniel的回答略有不同的方法。

I needed to expose the NestJS app at a specific path (eg '/api/v2') of the express "parent" app and passing the "parent" app to the ExpressAdapter (through app.middleware.ts ) was causing problems.我需要在 Express“父”应用程序的特定路径(例如“/api/v2”)处公开 NestJS 应用程序,并将“父”应用程序传递给 ExpressAdapter(通过app.middleware.ts )导致问题。 Also, I didn't want to start a new Nest instance on every request.另外,我不想在每个请求上都启动一个新的 Nest 实例。

Instead of the app.middleware.ts file, you can define a function (ie getExpressApp ) that creates a new express instance, uses that instance to create and initialize the NestJS app and returns the actual express instance.除了app.middleware.ts文件,您可以定义一个函数(即getExpressApp )来创建一个新的 express 实例,使用该实例来创建和初始化 NestJS 应用程序并返回实际的 express 实例。 You can then use that function in main.ts as follows:然后,您可以在main.ts中使用该函数,如下所示:

app.express.ts应用程序.express.ts

import { NestFactory } from '@nestjs/core';
import { ExpressAdapter } from '@nestjs/platform-express';
import * as express from 'express';
import { AppModule } from './app.module';

/** Creates and initializes a NestJS app and returns its raw express instance */
export const getExpressApp = () => {
  const app = express();
  return NestFactory.create(AppModule, new ExpressAdapter(app))
    .then((nestApp) => nestApp.init())
    .then(() => app);
};

main.ts main.ts

import * as express from 'express';
import {getExpressApp} from './app.express';

const parentApp = express();
// ... parentApp configuration

getExpressApp()
  .then(childApp => {
    parentApp.use('/api/v2', childApp);
  })
  .catch(() => {
    console.error('Error when mounting Nest server');
  })
  .finally(() => {
    parentApp.listen(3300)
  });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM