Would it be possible to add sample code for NestJS into https://github.com/vendia/serverless-express/tree/mainline/examples?
For example, how to do something like this with v4.0.0?
import { Server } from 'http'
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { Context, APIGatewayEvent } from 'aws-lambda'
import * as ServerlessExpress from '@vendia/serverless-express'
import express from 'express'
import { ExpressAdapter } from '@nestjs/platform-express'
let lambdaProxy : Server
async function bootstrap() {
const expressServer = express()
const app = await NestFactory.create(AppModule, new ExpressAdapter(expressServer))
await app.init()
return ServerlessExpress.createServer(expressServer)
}
export const handler = (event : APIGatewayEvent, context : Context) => {
if (!lambdaProxy) {
bootstrap().then((server) => {
lambdaProxy = server
ServerlessExpress.proxy(lambdaProxy, event, context)
})
} else {
ServerlessExpress.proxy(lambdaProxy, event, context)
}
}
Great idea! My file is similar:
lambda.ts
// transpiling https://github.com/claudiajs/claudia/issues/132
// no relative paths https://github.com/claudiajs/claudia/issues/40#issuecomment-218372727
// timeout https://stackoverflow.com/a/54086731
// aws gateway timeout https://github.com/vendia/serverless-express#considerations
// module.exports https://github.com/janaz/lambda-request-handler/pull/6
import type { Handler, Context } from 'aws-lambda';
import { Server } from 'http';
import { createServer, proxy } from 'aws-serverless-express';
import { eventContext } from 'aws-serverless-express/middleware';
import { NestFactory } from '@nestjs/core';
import {
ExpressAdapter,
NestExpressApplication,
} from '@nestjs/platform-express';
import { AppModule } from './app/app.module';
import { useGlobal } from './bootstrap';
// https://github.com/Microsoft/TypeScript/issues/13340
import express = require('express');
// NOTE: If you get ERR_CONTENT_DECODING_FAILED in your browser, this
// is likely due to a compressed response (e.g. gzip) which has not
// been handled correctly by aws-serverless-express and/or API
// Gateway. Add the necessary MIME types to binaryMimeTypes below
// https://github.com/awslabs/aws-serverless-express/blob/master/examples/basic-starter/lambda.js
const binaryMimeTypes: string[] = [
'application/javascript',
'application/json',
'application/octet-stream',
'application/xml',
'font/eot',
'font/opentype',
'font/otf',
'image/gif',
'image/heic',
'image/jpeg',
'image/png',
'image/svg+xml',
'image/webp',
'text/comma-separated-values',
'text/css',
'text/html',
'text/javascript',
'text/plain',
'text/text',
'text/xml',
];
let cachedServer: Server;
// Create the Nest.js server and convert it into an Express.js server
async function bootstrapServer(): Promise<Server> {
if (!cachedServer) {
const expressApp = express();
const nestApp = await NestFactory.create<NestExpressApplication>(
AppModule,
new ExpressAdapter(expressApp),
);
nestApp.use(eventContext());
// custom add global pipes, filters, interceptors, etc.
useGlobal(nestApp);
await nestApp.init();
cachedServer = createServer(expressApp, undefined, binaryMimeTypes);
}
return cachedServer;
}
// Export the handler : the entry point of the Lambda function
export const handler: Handler = async (event: any, context: Context) => {
// https://medium.com/safara-engineering/wiring-up-typeorm-with-serverless-5cc29a18824f
context.callbackWaitsForEmptyEventLoop = false;
cachedServer = await bootstrapServer();
return proxy(cachedServer, event, context, 'PROMISE').promise;
};
bootstrap.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app/app.module';
import { NestExpressApplication } from '@nestjs/platform-express';
import helmet from 'helmet';
export async function bootstrap(): Promise<NestExpressApplication> {
const app = await NestFactory.create<NestExpressApplication>(AppModule);
return app;
}
export async function useGlobal(
app: NestExpressApplication,
): Promise<NestExpressApplication> {
app.use(helmet());
app.disable('x-powered-by');
return app;
}
EDIT: I added a helper to an npm package I鈥檓 working on. Rough around the edges, but at least I have it written down somewhere 馃槃
I took a crack at it tonight, but wasn't able to get it to work yet. Here's where I'm at so far. Anyone else making progress?
```(typescript)
import { NestFactory } from '@nestjs/core';
import { ExpressAdapter } from '@nestjs/platform-express';
import serverlessExpress from '@vendia/serverless-express';
import { Context } from 'aws-lambda';
import express from 'express';
import { RequestListener } from 'http';
import { AppModule } from './app.module';
let cachedApp: RequestListener;
async function bootstrap() {
if (!cachedApp) {
const expressApp = express();
const nestApp = await NestFactory.create(
AppModule,
new ExpressAdapter(expressApp),
);
nestApp.enableCors();
await nestApp.init();
cachedApp = expressApp;
}
return cachedApp;
}
export const handler = async (event: any, context: Context, callback: any) => {
const app = await bootstrap();
const { handler } = serverlessExpress({ app });
handler(event, context, callback);
};
```
Here's the repo if you are interested in trying it out (yarn sls:offline to test).
https://github.com/michaelmerrill/nestjs-vendia
@michaelmerrill here鈥檚 a working example with sls-offline 馃槃
I did use my helper package, but it鈥檚 essentially similar code to what I posted above. I still use nest鈥檚 start:dev to write and debug my code, and I consider sls-offline similar to start:prod.
Here鈥檚 the serverless.yml
service: nestjs-with-sls
plugins:
- serverless-offline
provider:
name: aws
runtime: nodejs12.x
lambdaHashingVersion: 20201221
apiGateway:
shouldStartNameWithService: true
functions:
main: # The name of the lambda function
# The module 'handler' is exported in the file 'src/lambda'
handler: dist/lambda.handler
events:
- http:
method: any
path: /{any+}
And package.json scripts:
{
"presls:offline": "npm run build",
"sls:offline": "sls offline",
}
The rest follows the instructions here
thanks @jlarmstrongiv! Awesome work! Have you found a way to get this working with @vendia/serverless-express?
@michaelmerrill I wish @vendia/serverless-express was a drop in replacement for aws-serverless-express. Last I checked, I received either an error or incorrect typescript typings. So I thought it would be easier just to wait and switch over later once the moving dust settles 馃槀 It鈥檚 on the todo list
@vendia/serverless-express v3 should be a drop-in replacement. v4 is the breaking change. Is there a way to write a test that verifies Typescript typings?
@brettstack any advice on if we should be using proxy or handler for this case?
Alright, I think I ended up figuring it out. I've updated my repo. If I'm off or missing something let me know!
Nice! I got it working with npm i && sls deploy. Do you want to send a PR adding this to the examples directory here? A few things to update before that:
service: nestjs-vendia
...
provider:
name: aws
runtime: nodejs14.x
functions:
main: # The name of the lambda function
# The module 'handler' is exported in the file 'src/lambda'
handler: src/lambda.handler
events:
- http:
method: ANY
path: /
- http:
method: ANY
path: '{proxy+}'
"deploy": "sls deploy" script, npm i -D serverless, and update README?@michaelmerrill Thank you! That works like a charm! :+1:
@michaelmerrill did you want to send a PR for this or should I go ahead and add it and attribute?
I tried to create a pr, but husky is requiring tests to pass on the develop branch that are failing.
Looks like the integration test for the basic-starter example:
FAIL __tests__/integration.js
console.log src/index.js:123
ERROR: aws-serverless-express error
console.error src/index.js:124
TypeError: Cannot read property 'headers' of null
...
Sorry! Instructions are outdated. Please submit against the mainline branch
PR created. Please let me know if it needs updating.
As a side note, I find it odd that when using sls-offline I'm unable to hit a / route. The only way to get a successful response is by setting a path (ie /hello).
For example, if I create a new Nestjs application and start up the server locally I can visit localhost:3000 and get a 200. If I take that same application and access it via serverless with a path such as
functions:
main:
handler: src/lambda.handler
events:
- http:
method: ANY
path: /
Visiting localhost:3000/dev return a cannot GET null. I'm not sure if this is a serverless issue, nestjs issue, or something else.
I'll need to do more sls-offlinen testing
Thanks again! This is a great addition. I've seen a lot of people asking about NestJS
:tada: This issue has been resolved in version 4.2.0 :tada:
The release is available on:
Your semantic-release bot :package::rocket:
Hi, I use this snippet and i want support websocket gateway.
I already configure my serverless.yml. when i request my socket endpoint I receive the data of my request in the variable event, but it does not forward it to my gateway.
How resolve this?
```js import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { ExpressAdapter } from '@nestjs/platform-express';
import { APIGatewayProxyHandler } from 'aws-lambda';
import { createServer, proxy } from 'aws-serverless-express';
import { eventContext } from 'aws-serverless-express/middleware';
import express from 'express';
import { Server } from 'http';
import { AppModule } from './app.module';
let cachedServer: Server;
const bootstrapServer = async (): Promise
const expressApp = express();
expressApp.use(eventContext());
const app = await NestFactory.create(
AppModule,
new ExpressAdapter(expressApp),
);
app.useGlobalPipes(new ValidationPipe({ forbidUnknownValues: true }));
app.enableCors();
await app.init();
return createServer(expressApp);
};
export const handler: APIGatewayProxyHandler = async (event, context) => {
if (!cachedServer) {
cachedServer = await bootstrapServer();
}
console.log('---event', event);
// console.log('---context', context);
return proxy(cachedServer, event, context, 'PROMISE').promise;
};
Most helpful comment
Alright, I think I ended up figuring it out. I've updated my repo. If I'm off or missing something let me know!