Skip to content

Support API Gateway proxy Lambda integrations #193

@brendo-m

Description

@brendo-m

The most common usage of API GW and Lambda is to configure routes backed by Lambda proxy integrations. Proxy here just means that the whole request that APIGW sees is forwarded to the Lambda function. You can see it's type here

See also https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-api-integration-types.html

My current prototype looks like this:

export type ApiGatewayProxyRequest<
  Request extends ApiRequest<any, any, any, any> // targs for path, query string, headers and body
> = Omit<
  APIGatewayProxyEvent,
  "pathParameters" | "queryStringParameters" | "headers" | "body"
> & {
  pathParameters: Request["pathParameters"];
  queryStringParameters: Request["queryStringParameters"];
  headers: Request["headers"];
  body: Request["body"];
};

type ProxyFunction<Request extends ApiRequest<any, any, any, any>> = Function<
  ApiGatewayProxyRequest<Request>,
  APIGatewayProxyResult
>;

interface ExampleRequest {
  pathParameters: {
    num: number;
  };
}

const httpFn: ProxyFunction<ExampleRequest> = new Function(
  stack,
  "httpFn",
  async (event) => {
    return {
      statusCode: 200,
      body: JSON.stringify({
        id: event.pathParameters.num,
      }),
    };
  }
);

const lambdaProxyResource = restApi.root
  .addResource("lambda-proxy")
  .addResource("{num}");
const lambdaProxyIntegration = ApiIntegrations.lambda({
  fn: httpFn,
});
lambdaProxyIntegration.addMethod("GET", lambdaProxyResource);

This gives us parameter types for the Lambda function code to work with.

The problem though is that the body arg API GW sends to Lambda is a string, so the above code won't work. We need some way to preprocess or inject the parsed body into the Lambda input event, or some other mechanism for parsing it while keeping a clean DX.

Some options we discussed:

  • Fork Function into a new HttpFunction class. The compiler plugin would inject code to parse the body e.g. event = { ...event, body: JSON.parse(event.body) }
  • Heuristically detect whether a Function is backing an api integration and inject code to parse the body
  • Add ability for user to add middleware into the Function code
  • Force user to deal with it themselves e.g. add const body = event.body as BodyType to their functions

Middleware option is interesting because it adds value beyond just solving this problem. It is quite common to use middleware for setting up logging, Xray integration etc.

It might look something like this:

new Function(this, "fn", async (event) => event.foo)
  .withMiddleware((event) => ({ ...event, body: JSON.parse(body) }))
  .withMiddleware((event) => {
    XRay.configure(event);
    return event;
  });

Metadata

Metadata

Assignees

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions