Import a Nest.js app as a simple Express middleware

While I don't condone the use of Nest as a middleware itself, it is possible. 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.

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

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

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

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

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

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.

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. Just make sure to remove any error handling middleware as it will start to conflict with how Nest handles responses. You should instead move those functions to 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.

4) The error that was coming in as [Object object] in case you were wondering, was a standard Express error Cannot GET /. Dunno why it was serialized strangely, but a JSON.stringify() in the catch helped resolve it.

Overall, I would not recommend this approach but it is possible to do.