Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use with NestJS #115

Open
2 tasks done
rbgfloripa opened this issue Jul 1, 2022 · 20 comments
Open
2 tasks done

Use with NestJS #115

rbgfloripa opened this issue Jul 1, 2022 · 20 comments

Comments

@rbgfloripa
Copy link

Prerequisites

  • I have written a descriptive issue title
  • 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.

@adrai
Copy link
Member

adrai commented Jul 1, 2022

maybe @cefamax can help?

btw: if an old version of aws-lambda-fastify worked, the newest version should still work

@rbgfloripa
Copy link
Author

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.

@adrai
Copy link
Member

adrai commented Jul 4, 2022

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.

@rbgfloripa
Copy link
Author

Yeah... I already tried :-(
I didn't find it anywhere
But thanks man.
I will keep trying to make it work.

@cefamax
Copy link

cefamax commented Jul 5, 2022

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 {

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 => {

if (!cachedProxy) {

    if (!cachedNestApp) {
        cachedNestApp = await bootstrapServer();
    }
    cachedProxy = awsLambdaFastify(cachedNestApp.instance, { decorateRequest: true });
}

return cachedProxy(event, context);

};
`

@rbgfloripa
Copy link
Author

Thanks a lot man.
I'll test it right now.

@dustingraves
Copy link

Anyone using v3 with nestjs?

@Jwagner347
Copy link

@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?

@cefamax
Copy link

cefamax commented Dec 15, 2022

@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.

@Uzlopak
Copy link
Contributor

Uzlopak commented Dec 15, 2022

aws-samples/aws-lambda-elixir-runtime#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/

@Jwagner347
Copy link

@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.

@Jwagner347
Copy link

@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?

@Uzlopak
Copy link
Contributor

Uzlopak commented Dec 15, 2022

@Jwagner347

Can you improve the readme.md to persist that knowledge?

@cefamax
Copy link

cefamax commented Dec 16, 2022

@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.

@climba03003
Copy link
Member

climba03003 commented Dec 16, 2022

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.

@Jwagner347
Copy link

Jwagner347 commented Dec 16, 2022

@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.

@cefamax
Copy link

cefamax commented Dec 16, 2022

@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!

@Jwagner347
Copy link

@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.

@toptal-dave
Copy link

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.

@toptal-dave
Copy link

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...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants