aws-lambda-fastify
aws-lambda-fastify copied to clipboard
Use with NestJS
Prerequisites
- [X] I have written a descriptive issue title
- [X] I have searched existing issues to ensure the issue has not already been raised
Issue
Hi all,
Does anyone already use it in production with NestJS? Is it possible to send me an example?
I searched and didn't find any information about this. The only article I found (on Medium) uses old versions.
TIA,
Rafael.
maybe @cefamax can help?
btw: if an old version of aws-lambda-fastify worked, the newest version should still work
Hi,
Yes, old version works, but with old versions of nest. The example code no longer works. So I wanted some help to test it with current versions (nest 8+).
Thanks
Rafael.
Hi,
Yes, old version works, but with old versions of nest. The example code no longer works. So I wanted some help to test it with current versions (nest 8+).
Thanks
Rafael.
Sorry, I'm not a nest user, you may try to ask at the nest community or stackoverflow.
Yeah... I already tried :-( I didn't find it anywhere But thanks man. I will keep trying to make it work.
hi @rbgfloripa, I write you the lambda.ts file contents. I use in production "aws-lambda-fastify": "^2.2.0" nodejs 16.x. I hope it helps you. ` import { NestFactory } from '@nestjs/core'; import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify'; import { FastifyServerOptions, FastifyInstance, fastify } from 'fastify'; import awsLambdaFastify from 'aws-lambda-fastify'; import { AppModule } from './app.module'; import { Context, APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda'; import { Logger, LogLevel } from '@nestjs/common';
interface NestApp { app: NestFastifyApplication; instance: FastifyInstance; }
let cachedNestApp: NestApp; let cachedProxy;
async function bootstrapServer(): Promise<NestApp> {
const serverOptions: FastifyServerOptions = {
logger: (process.env.LOGGER || '0') == '1',
};
const instance: FastifyInstance = fastify(serverOptions);
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter(instance),
{
logger: !process.env.AWS_EXECUTION_ENV ? new Logger() : console,
},
);
const CORS_OPTIONS = {
origin: '*',
allowedHeaders: '*',
exposedHeaders: '*',
credentials: false,
methods: ['GET', 'PUT', 'OPTIONS', 'POST', 'DELETE'],
};
app.register(require('fastify-cors'), CORS_OPTIONS);
app.setGlobalPrefix(process.env.API_PREFIX);
await app.init();
return {
app,
instance
};
}
export const handler = async (event: APIGatewayProxyEvent, context: Context): Promise<APIGatewayProxyResult> => {
if (!cachedProxy) {
if (!cachedNestApp) {
cachedNestApp = await bootstrapServer();
}
cachedProxy = awsLambdaFastify(cachedNestApp.instance, { decorateRequest: true });
}
return cachedProxy(event, context);
}; `
Thanks a lot man. I'll test it right now.
Anyone using v3 with nestjs?
@cefamax I was able to get my fastify/mercurius graphql app running locally through serverless-offline
thanks to your suggestion above; however, when I deployed to AWS, I am getting an error: EROFS: read-only file system, mkdir '/var/task/src'
.
I don't know where Nest is trying to write to the lambda file system but happens right after INFO Mapped {/, GET} route RouterExplorer
in the logs. Any idea where this might be happening and how to fix?
@Jwagner347 I have no idea what it could be, with the information you wrote. Nest shouldn't write anything to that folder. You could try building a clean nestjs application to see if it works in aws and then add the other components to check if anything outside of nest can give that problem.
https://github.com/aws-samples/aws-lambda-elixir-runtime/issues/7 https://stackoverflow.com/questions/53810516/getting-error-aws-lambda-erofs-read-only-file-system-open-var-task-assets https://stackoverflow.com/questions/49020710/lambda-enoent-no-such-file-or-directory
/var/task
is where your Lambda function code is located, and in the actual Lambda environment, that filesystem is read-only. If you need to write to a file, you need to write to/tmp
.Q: What if I need scratch space on disk for my AWS Lambda function?
Each Lambda function receives 500MB of non-persistent disk space in its own /tmp directory.
https://aws.amazon.com/lambda/faqs/
@Uzlopak I know you can only write to /tmp
in lambda, my issue is that I don't know why NestJS application is trying to write in the first place. It looks like when I call app.init()
I get that error.
@cefamax I was able to figure it out. It's a GraphQL application, and I was using autoSchemaFile
, which was trying to write the schema file on initialization. I just had to change it to only run locally and then copy over the schema during my build before deploying to lambda.
Thanks for your help, the code you initially posted helped me a lot to get up and running with fastify/mercurius in lambda. One thing I don't understand though is you are caching the proxy in addition to the app. Everywhere else I see proxy is just called directly. What is the reason for this?
@Jwagner347
Can you improve the readme.md to persist that knowledge?
@Jwagner347 I had read a comment by @mcollina where he recommends doing it, but now I can't find it to send it to you. However I have been using this system for several months, I have never had any problems.
What is the reason for this?
In most of the server-less environment, handler
will be called everytime for a request and anything outside may cached between calls (determined by how long the environment may spin down).
By using a variables cache, it will minimize the cold-start time of fastify
since it will be construct once vs construct everytime.
@climba03003 but why not do it like this:
export const handler = async (
event: APIGatewayProxyEvent,
context: Context,
): Promise<PromiseHandler> => {
if (!cachedNestApp) {
const nestApp = await bootstrap();
cachedNestApp = awsLambdaFastify(nestApp.instance, {
decorateRequest: true,
});
}
return cachedNestApp(event, context);
};
Why cache proxy separate from the bootstrapped app? I would think that if one variable was removed from lambda cache, all variables would since you say it is determined by how long the environment spin down is set for. I am not trying to be pedantic, genuinely trying to understand where I may be missing something.
@Jwagner347 I understand what you mean. It seems to me that the code you wrote is better than what I had written and the same result is achieved. Thank you!
@Uzlopak I have a PR in that updates the Readme.
I combined the suggestions and information from @cefamax and @climba03003, so let me know if I should change anything there.
Just updating here that the latest version of Nest requires some updates to the code in this issue:
import { NestFactory } from '@nestjs/core';
import {
FastifyAdapter,
NestFastifyApplication,
} from '@nestjs/platform-fastify';
import { AppModule } from './app.module';
import awsLambdaFastify, { PromiseHandler } from '@fastify/aws-lambda';
import { Context, APIGatewayProxyEvent } from 'aws-lambda';
import { Logger } from '@nestjs/common';
let cachedNestApp;
async function bootstrap(): Promise<NestFastifyApplication> {
// Create the app
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
// Use an env var to set the logger to nest or console
new FastifyAdapter({ logger: (process.env.LOGGER || '0') == '1' }),
{
logger: !process.env.AWS_EXECUTION_ENV ? new Logger() : console,
},
);
// Enable cors
app.enableCors({
origin: '*',
allowedHeaders: '*',
exposedHeaders: '*',
credentials: false,
methods: ['GET', 'PUT', 'OPTIONS', 'POST', 'DELETE'],
});
// Set the prefix as necessary
app.setGlobalPrefix(process.env.API_PREFIX);
await app.init();
return app;
}
export const handler = async (
event: APIGatewayProxyEvent,
context: Context,
): Promise<PromiseHandler> => {
// If there's no cached app
if (!cachedNestApp) {
// Bootstrap
const nestApp: NestFastifyApplication = await bootstrap();
// Create an AWS Lambda Fastify cached app from the Nest app
cachedNestApp = awsLambdaFastify(nestApp.getHttpAdapter().getHttpServer(), {
decorateRequest: true,
});
}
return cachedNestApp(event, context);
};
I do think this should be somewhere on the web but I'm not sure if it belongs in Nest or here. To be honest, if you are worried about noisiness of the documentation, this is pretty sparse compared to Nest's documentation so it wouldn't be too noisy here comparatively. At the same time, I find their serverless documentation to be a bit underwhelming.
Looks like I'm still running into a problem:
My set up is using SST to deploy a containerized Nest.js application using this plugin. There's a demo repo here. I get the following error when I hit the Lambda URL:
2023-12-31T23:36:48.020Z 5f37cec7-815e-4f51-a59d-7a20e93c69cf ERROR Invoke Error {
"errorType": "TypeError",
"errorMessage": "app.decorateRequest is not a function",
"stack": [
"TypeError: app.decorateRequest is not a function",
" at module.exports (/var/task/node_modules/@fastify/aws-lambda/index.js:23:9)",
" at Runtime.handler (/var/task/dist/main.js:28:50)",
" at process.processTicksAndRejections (node:internal/process/task_queues:95:5)"
]
}
Perhaps using getHttpAdapter().getHttpServer()
isn't the way to do it...