CodeGenieApp / serverless-express

Run Express and other Node.js frameworks on AWS Serverless technologies such as Lambda, API Gateway, Lambda@Edge, and more.
https://codegenie.codes
Apache License 2.0
5.13k stars 668 forks source link

Cold Start Trick with new v4 module #429

Open Muthuveerappanv opened 3 years ago

Muthuveerappanv commented 3 years ago

I saw this interesting medium post based on aws-serverless-express that helps with cold-start in nestjs by moving the bootstrap part to the init stage and not within the handler, like shown below image

Need help with translating this to the new v4 way of bootstrapping the express server using nestjs as the proxy is deprecated.

brettstack commented 3 years ago

Check out this example https://github.com/vendia/serverless-express/blob/mainline/examples/sails-example/lambda.js. Let me know if you need more guidance.

Muthuveerappanv commented 3 years ago

Check out this example https://github.com/vendia/serverless-express/blob/mainline/examples/sails-example/lambda.js. Let me know if you need more guidance.

not clear on how to make use of the proxy in the new v4 stack. Also in the above example the bootstrap is in the init phase and not in the handler, looking for help with that specifically.

jsefiani commented 3 years ago

@Muthuveerappanv Did you figure it out?

Muthuveerappanv commented 3 years ago

@Muthuveerappanv Did you figure it out?

Nope 😒

jsefiani commented 3 years ago

@brettstack Can you please provide more guidance on this topic? Because the example that you provided is lacking this.

brettstack commented 3 years ago

What's missing in the Nest example that you need? https://github.com/vendia/serverless-express/blob/mainline/examples/basic-starter-nestjs/src/lambda.ts

brettstack commented 3 years ago

Can you use serverlessExpress(...).prox(...)? See https://github.com/vendia/serverless-express/blob/mainline/src/configure.js#L30

jsefiani commented 2 years ago

Hey @brettstack, sorry for my late answer, totally forgot about it.

With the new version you can't have something like the following code example:

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

let cachedServer: Server;

const bootstrapServer = async (): Promise<Server> => {
  const expressApp = express();
  const adapter = new ExpressAdapter(expressApp);
  const app = await NestFactory.create(AppModule, adapter);
  app.enableCors();
  await app.init();
  return awsServerlessExpress.createServer(expressApp); // this method doesn't exist anymore
}

export const handler: APIGatewayProxyHandler = async (event, context) => {
  if (!cachedServer) {
    cachedServer = await bootstrapServer()
  }
  return awsServerlessExpress.proxy(cachedServer, event, context, 'PROMISE')
      .promise;
};

So my question is: how can I make above code compatible with v4 of serverless-express?

samjmarshall commented 2 years ago

Hey @Muthuveerappanv, after trying a few different implementations, I've managed to get a working updated solution for my services:

import { APIGatewayEvent, Callback, Context, Handler } from 'aws-lambda';

import { AppModule } from './app.module';
import { NestFactory } from '@nestjs/core';
import serverlessExpress from '@vendia/serverless-express';

let server: Handler;

async function bootstrap(): Promise<Handler> {
  const nestApp = await NestFactory.create(AppModule);
  await nestApp.init();
  const app = nestApp.getHttpAdapter().getInstance();
  return serverlessExpress({ app });
}

bootstrap().then((handler) => (server = handler));

function waitForServer(
  event: any,
  context: any,
  callback: Callback,
): Promise<any> {
  return new Promise((resolve) =>
    setImmediate(async () => {
      if (!server) {
        resolve(await waitForServer(event, context, callback));
      } else {
        resolve(server(event, context, callback));
      }
    }),
  );
}

export const handler: Handler = async (
  event: APIGatewayEvent,
  context: Context,
  callback: Callback,
): Promise<any> => {
  if (server) return server(event, context, callback);
  return await waitForServer(event, context, callback);
};

This seems to be working fine for me at the moment. But occasionally I'll see an initial DB connection error in the logs on startup, although it always immediately retries and succeeds.

ERROR [TypeOrmModule] Unable to connect to the database. Retrying (1)...`

To be honest the error seems to have little impact, when it occurs. I'm guessing this occasional startup error could be caused by the container network services simply not being ready in time. 🤷