Nest: Mounting an app

Created on 22 Nov 2017  路  8Comments  路  Source: nestjs/nest

Hi, i've tried all now:

  • Middleware
  • Controller
  • Component

How do you mount an app in this framework? It's the most simple thing in express app.use(otherApp).
As I understand Middlewares are supposed to be just that, but with more nest features. So i try consumer.apply(mySubApp).forRoutes({path: '/', method: RequestMethod.ALL }); in my AppModule but it doesn't work. Help would be appreciated!

Is it possible to just inject the express app into the module? To bypass some of the magic?

question 馃檶

Most helpful comment

Now NestApplication provides a set of wrappers around express native methods like use() or set().

All 8 comments

Hi @Kamshak,
Could you share your module file? 馃槂

sure

import { AppSettings } from './app.settings';
import { DeploymentController } from './deployment/deployment.controller';
import { KubernetesComponent } from './kubernetes.component';
import { Module, MiddlewaresConsumer, RequestMethod, HttpStatus } from '@nestjs/common';
import { PrerenderComponent } from './prerender/prerender.component';
import { SiteDeploymentComponent } from './deployment/site-deployment.component';
import { MetaController } from './meta.controller';
import { HttpException } from '@nestjs/core';
import { ArenaController } from './arena.module';
import * as Arena from 'bull-arena';
import { Observable } from 'rxjs';
import { interval } from "rxjs/observable/interval";
import { flatMap, map } from "rxjs/operators";
import * as express from 'express';

@Module({
  modules: [],
  components: [
    KubernetesComponent,
    PrerenderComponent,
    SiteDeploymentComponent,
    AppSettings
  ],
  controllers: [
    DeploymentController,
    MetaController,
    ArenaController
  ],
  exports: [ KubernetesComponent ]
})
export class ApplicationModule {
  arena: express.Application;

  constructor(
    private appSettings: AppSettings,
    private kube: KubernetesComponent
  ) {
    this.arena = Arena({}, { disableListen: true });

    const queueNames$ = interval(500).pipe(
      flatMap(() => this.kube.batchApi.listNamespacedJob(this.appSettings.kubernetes.namespace, null, null, null, null, "prerender-queue.type=worker")),
      map(result => result.body.items.map(item => {
          item.metadata.labels['prerender-queue.queue-name']
      }))
    ).subscribe(queueList => {
      const queueConfig = queueList.map(queueName => ({
        type: 'bee',
        name: queueName,
        url: this.appSettings.redisClient
      }));

      this.arena.locals.Queues.setConfig(queueConfig);
    }); 
  }

  configure(consumer: MiddlewaresConsumer) {
    consumer.apply((req, res, next) => {
      if (req.query.apiKey === this.appSettings.apiKey) {
        next();
      } else {
        next(new HttpException('Unauthorized', HttpStatus.UNAUTHORIZED));
      }
    }).forRoutes({ path: '/deployments', method: RequestMethod.ALL });

    consumer.apply(this.arena).forRoutes({path: '/', method: RequestMethod.ALL });
  }
}

@Kamshak let's try to move all the code from constructor to configure

Doesn't change it unfortunately. In the end it just creates the app in constructor then applies it a bit further down.

What I've done for now is just put the app as a static member and initialize it in the constructor (since i need some DI things to configure it). I then mount it in server.ts where the app is bootstrapped. Not pretty but it works.

Still wonder why it doesn't work as normal middleware, any clue? You can test this quite easily by simply doing something like

const app = express.app();
app.get('/hello', (req, res) => res.send("hello world"));
consumer.apply(app).forRoutes({path: '/', method: RequestMethod.ALL });

Now NestApplication provides a set of wrappers around express native methods like use() or set().

@kamilmysliwiec

Now NestApplication provides a set of wrappers around express native methods like use() or set().

I'm using app.use method to mount Arena as middleware but it hangs and app.listen is never reached.

This looks like a bug. Could you take it a look? thanks Kamil.

My code:

import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import * as helmet from 'helmet';
import * as bodyParser from 'body-parser';
import * as compression from 'compression';
import * as rateLimit from 'express-rate-limit';
import * as Sentry from '@sentry/node';
import Arena from 'bull-arena';

import { AppModule } from './modules/app/app.module';
import { Logger } from './common/logger';
import APP_CONFIG from './modules/app/app.config';

const logger = new Logger('Bootstrap');
const { PORT, HOST, SENTRY_DSN, APP_MODE, SERVER_NAME } = APP_CONFIG;

async function bootstrap() {
  Sentry.init({
    dsn: SENTRY_DSN,
    environment: APP_MODE,
    serverName: SERVER_NAME,
  });
  const app = await NestFactory.create<NestExpressApplication>(AppModule);

  app.enableCors();
  app.use(helmet());
  app.use(compression());
  app.use(bodyParser.json({ limit: '1mb' }));
  app.use(bodyParser.urlencoded({ extended: true }));
  app.use(new rateLimit({ max: 1000, windowMs: 15 * 60 * 1000 }));

  app.use(
    '/bull-arena',
    Arena(
      {
        queues: [
          {
            name: 'queue',
            hostId: 'Worker',
            port: 6381,
            host: '127.0.0.1',
          },
        ],
      },
      {
        disableListen: true,
      },
    ),
  );

  await app.listen(PORT, HOST, () => {
    logger.info(`listening on ${HOST}:${PORT}`);
  });
}

bootstrap();

@alfonmga try to add basePath option in the listening object
it'll looks like :

app.use(
    '/bull-arena',
    Arena(
      {
        queues: [
          {
            name: 'queue',
            hostId: 'Worker',
            port: 6381,
            host: '127.0.0.1',
          },
        ],
      },
      {
        basePath: '/'
        disableListen: true,
      },
    ),
  );

Now you you should be able to access you arena dashboard from '/bull-arena/' unless there is
other problem e.g redis connection not correctly configured.

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ArsalaBangash picture ArsalaBangash  路  27Comments

hypeofpipe picture hypeofpipe  路  27Comments

MonsieurMan picture MonsieurMan  路  28Comments

mogusbi picture mogusbi  路  53Comments

roeehershko picture roeehershko  路  27Comments