serverless-http icon indicating copy to clipboard operation
serverless-http copied to clipboard

serverless-http + Nest.js

Open dzmitrypanamarenka opened this issue 4 years ago • 3 comments

Hi guys! Is there a way to use Nest.js with this awesome package?

dzmitrypanamarenka avatar Mar 07 '20 17:03 dzmitrypanamarenka

@dzmitrypanamarenka so here's how I'm doing it, although this has only been partially successful for me:

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

const server = express();

export const createNestServer = async expressInstance => {
  const app = await NestFactory.create(AppModule, new ExpressAdapter(expressInstance));
  app.enableCors();
  return app.init();
};

createNestServer(server)
  .then(v => console.log('Nest Ready'))
  .catch(err => console.error('Nest broken', err));

export const handler = serverless(server);

The two issues I continue to have are:

  1. Nest only builds and bootstraps after an API call has come in
  2. Even though it says "watching for changes" and I see it updating when I change a file, Nest doesn't re-bootstrap after the files change.

The result of this is that you have to hit the app twice in order to see the real response and then if you change a file you have to stop it and start it. I would love to know if my above code could be improved to fix these things!

mcblum avatar Apr 03 '20 14:04 mcblum

Hey guys,

I do this:

lambda.ts:

import { Context, Handler } from 'aws-lambda'
import { proxyApi } from '../lib/bootstrap-api'
import { ApiModule } from './api.module'

export const handler: Handler = async (event: any, context: Context) =>
  proxyApi(ApiModule, event, context)

bootstrap-api.ts:

import './configure-module-aliases'

import { ValidationPipe } from '@nestjs/common'
import { NestFactory } from '@nestjs/core'
import { ExpressAdapter } from '@nestjs/platform-express'
import { Context } from 'aws-lambda'
import express, { Express } from 'express'
import { RavenInterceptor } from 'nest-raven'
import serverless from 'serverless-http'
import {
  initializeTransactionalContext,
  patchTypeORMRepositoryWithBaseRepository,
} from 'typeorm-transactional-cls-hooked'
import { registerSentry } from './sentry'

export const bootstrapApi = async (module: any) => {
  registerSentry()
  initializeTransactionalContext()
  patchTypeORMRepositoryWithBaseRepository()

  const expressApp = express()

  const nestApp = await NestFactory.create(
    module,
    new ExpressAdapter(expressApp),
  )

  nestApp.enableCors()
  nestApp.useGlobalPipes(new ValidationPipe())
  nestApp.useGlobalInterceptors(
    new RavenInterceptor({
      context: 'GraphQL',
    }),
  )

  await nestApp.init()

  return { nestApp, expressApp }
}

let cachedHandler: any
export const proxyApi = async (
  rootModule: any,
  event: any,
  context: Context,
) => {
  try {
    if (!cachedHandler) {
      const { expressApp } = await bootstrapApi(rootModule)
      cachedHandler = serverless(expressApp)
    }
  } catch (err) {
    console.error('Error bootstrapping API', err)
    throw err
  }
  return cachedHandler(event, context)
}

TreeMan360 avatar Apr 04 '20 14:04 TreeMan360

@TreeMan360 I like how you did it it seems fairly clean. I think you have an error though:

export const handler: Handler = async (event: any, context: Context) =>
  proxyApi(ApiModule, event, context)

You don't need an async here. For me running this with serverless offline gives an error about using a promise and a callback.

morganabel avatar May 25 '20 17:05 morganabel