2

I have a basic Serverless Express app in a lambda, with a route set to async true. I want to trigger this route asynchronously from a different application, and expect it to run in the background without having to wait for the response.

My full serverless.yml

service: service-name
useDotenv: true

custom:
  serverless-offline:
    useChildProcesses: true
  webpack:
    webpackConfig: ./webpack.config.js
    packager: "yarn"
    includeModules:
      forceExclude:
        - aws-sdk
  prune:
    automatic: true
    includeLayers: true
    number: 3
  envStage:
    staging: staging
  domainPrefix:
    staging: service.staging
  customDomain:
    domainName: ${self:custom.domainPrefix.${opt:stage}}.mydomain.com 
    basePath: ""
    stage: ${self:custom.envStage.${opt:stage}}
    createRoute53Record: true

plugins:
  - serverless-domain-manager
  - serverless-webpack
  - serverless-prune-plugin
  - serverless-offline

provider:
  lambdaHashingVersion: "20201221"
  name: aws
  runtime: nodejs14.x
  region: us-east-1
  apiGateway:
    minimumCompressionSize: 1024 
  iamRoleStatements:
    - Effect: Allow
      Action: ssm:Get*
      Resource: "arn:aws:ssm:*:*:parameter/myparams/*"
    - Effect: Allow
      Action: kms:Decrypt
      Resource: "*"

functions:
  express:
    handler: src/index.middyHandler
    events:
      - http:
          path: /
          method: options
      - http:
          path: /{any+} # Catch all routes
          method: options
      - http:
          path: foo/{any+}
          method: get
      - http:
          path: foo/{any+}
          method: post
          async: true


Note: The role that deploys this app has permissions to read write to Cloudwatch, and I can see logs from the synchronous invocations, but not from async invocations.

My index.middyHandler

import serverless from "serverless-http";
import express from "express";
import helmet from "helmet";
import bodyParser from "body-parser";
import cookieParser from "cookie-parser";
import middy from "@middy/core";
import ssm from "@middy/ssm";
import doNotWaitForEmptyEventLoop from "@middy/do-not-wait-for-empty-event-loop";
import cors from "cors";
import fooRoutes from "./routes/foo";

const app = express();

app.use(
  cors({
    methods: "GET,HEAD,OPTIONS,POST",
    preflightContinue: false,
    credentials: true,
    origin: true,
    optionsSuccessStatus: 204,
  })
);

app.use(helmet({ contentSecurityPolicy: false, crossOriginEmbedderPolicy: false }));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cookieParser());

app.get("/ping", (req, res) => {
  res.send("Pong!");
});

// Register routes
app.use("/foo", fooRoutes);

const handler = serverless(app);

export const middyHandler = middy(handler)
  .use(
    doNotWaitForEmptyEventLoop({
      runOnError: true,
      runOnAfter: true,
      runOnBefore: true,
    })
  )
  .use(
    ssm({
      setToEnv: true,
      fetchData: {
       MY_KEYS: "ssm/path"
      },
    })
  )

When I call this method, it correctly returns a 200 response immediately. But the actual code is never run, I have a DB insert in there, and it doesn't happen. In the API Gateway I can see the X-Amz-Invocation-Type header is correctly being passed as Event type.

Integration Request HTTP headers

It is not a proxy integration, as required for async invocation Integration Request type

What am I missing here? The route controller is a test and the code is very simple

 testAsync: async (req, res) => {
    console.log("In Test Async");  // Does not display in Cloudwatch
    try {
      const { value } = req.body;
      const resp = await updateTest(value);  // This just inserts an entry in the DB with value
      return res.send(resp);
    } catch (err) {
      return res.status(500).send(err);
    }
  },

Is there any other setting I'm missing here? I'm not an AWS expert, so any help would be highly appreciated. Thanks!

kvnam
  • 1,285
  • 2
  • 19
  • 34
  • 1
    What are the cloudwatch logs saying for the invocations? Could you console.log between the lines? The async apigateway invocation will always return 200 regardless so I suspect maybe it's stopping at the line it does the insert in. – Majdie Gideir Jun 04 '22 at 21:48
  • @MajdieGideir- I did add console logs and none of those get printed in the Cloudwatch logs. I even have a log as the first line in the route handler, even that is not getting printed in Cloudwatch. The API does return 200, but it seems like it never calls the actual handler. Just a note I have other routes in here which are not async, would that make a difference? – kvnam Jun 06 '22 at 10:57
  • Can you show your full serverless.yaml? Do you have the right IAM permissions etc.? – Ermiya Eskandary Jun 08 '22 at 20:44
  • 1
    @ErmiyaEskandary - I updated the issues with the full serverless.yml. As mentioned the role that deploys this app has permissions to read write to Cloudwatch, and I can see logs from the synchronous invocations, but not from async invocations. What special permissions do I need to async invocation? Everything else works as expected so the permissions are in place up till that point. – kvnam Jun 09 '22 at 12:41
  • Excuse my ignorance. The name `testAsync` appears nowhere in `serverless.yml`. How can the server know which Javascript function to invoke when the request comes in? Is this somewhere in the `src/index.middyHandler`? – Heiko Theißen Jun 12 '22 at 13:02
  • Are you trying to invoke multiple lambdas or handle the routes in the same Lambda? – Ermiya Eskandary Jun 12 '22 at 13:24
  • @ErmiyaEskandary- Just a single lambda handling all the routes, including the async one. This is invoked by another lambda which is my primary backend (serverless-express app). I'm trying to trigger async calls to this lambda from my backend lambda. There are no async routes in my backend, it's a Graphql server. – kvnam Jun 13 '22 at 11:50
  • What does your `index.middyHandler` look like then? – Ermiya Eskandary Jun 13 '22 at 13:08
  • 1
    @HeikoTheißen- The `foo/{any+}` path will cover the `foo/testAsync` as well. – kvnam Jun 13 '22 at 17:09
  • @ErmiyaEskandary - I updated the post with my index.js. – kvnam Jun 13 '22 at 17:09

0 Answers0