Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How get path params in CDK + APIGateway + Lambda

So, turns out I had it all along but I was logging it out incorrectly. I had been doing a Object.keys(event).forEach and console logging each key and value. I guess this didn't display the value as it's a nested object. Using JSON.stringify, as per @robC3's answer shows all the nested objects and values properly and is easier too! TL;DR just use curly braces in your gateway paths and they will be present in event.pathParameters.whateverYouCalledThem

I'm used to express land where you just write /stuff/:things in your route and then req.params.things becomes available in your handler for 'stuff'.

I'm struggling to get the same basic functionality in CDK. I have a RestAPI called 'api' and resources like so...

const api = new apigateway.RestApi(this, "image-cache-api", { //options })
const stuff = api.root.addResource("stuff")
const stuffWithId = get.addResource("{id}")
stuffWithId.addMethod("GET", new apigateway.LambdaIntegration(stuffLambda, options))

Then I deploy the function and call it at https://<api path>/stuff/1234

Then in my lambda I check event.pathParameters and it is this: {id: undefined}

I've had a look through the event object and the only place I can see 1234 is in the path /stuff/1234 and while I could just parse it out of that I'm sure that's not how it's supposed to work.

:/

Most of the things I have turned up while googling mention "mapping templates". That seems overly complicated for such a common use case so I had been working to the assumption there would be some sort of default mapping. Now I'm starting to think there isn't. Do I really have to specify a mapping template just to get access to path params and, if so, where should it go in my CDK stack code?

I tried the following...

stuffWithId.addMethod("GET", new apigateway.LambdaIntegration(stuffLambda, {
    requestTemplate: {
        "id": "$input.params('id')",
    }        
}))

But got the error...

error TS2559: Type '{ requestTemplate: { id: string; }; }' has no properties in common with type 'LambdaIntegrationOptions'.

I'm pretty confused as to whether I need requestTemplate, requestParametes, or something else entirely as all the examples I have found so far are for the console rather than CDK.

like image 746
Roger Heathcote Avatar asked Mar 30 '26 19:03

Roger Heathcote


1 Answers

This works fine, and you can see where the full path, path params, query params, etc., are in the event structure when you test it in a browser.

// lambdas/handler.ts

// This code uses @types/aws-lambda for typescript convenience.
// Build first, and then deploy the .js handler.

import { APIGatewayProxyHandler, APIGatewayProxyResult } from 'aws-lambda';

export const main: APIGatewayProxyHandler = async (event, context, callback) => {
    return <APIGatewayProxyResult> {
        body: JSON.stringify([ event, context ], null, 4),
        statusCode: 200,
    };
}

// apig-lambda-proxy-demo-stack.ts

import * as path from 'path';
import { aws_apigateway, aws_lambda, Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class ApigLambdaProxyDemoStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const stuffLambda = new aws_lambda.Function(this, 'stuff-lambda', {
        code: aws_lambda.Code.fromAsset(path.join('dist', 'lambdas')),
        handler: 'handler.main',
        runtime: aws_lambda.Runtime.NODEJS_14_X,
    });

    const api = new aws_apigateway.RestApi(this, 'image-cache-api');

    const stuff = api.root.addResource('stuff');
    const stuffWithId = stuff.addResource('{id}');
    stuffWithId.addMethod('GET', new aws_apigateway.LambdaIntegration(stuffLambda));
  }
}

This sample query:

https://[id].execute-api.[region].amazonaws.com/prod/stuff/1234?q1=foo&q2=bar

gives this response (excerpt):

Sample API Gateway test lambda response JSON

If you want to handle arbitrary paths at a certain point in your API, you'll want to explore the IResource.addProxy() CDK method. For example,

api.root.addProxy({
    defaultIntegration: new aws_apigateway.LambdaIntegration(stuffLambda),
});

That creates a {proxy+} resource at the API root in the example and would forward all requests to the lambda. Rather than configuring every single endpoint in API Gateway, you could handle them all in your same handler.

like image 101
rob3c Avatar answered Apr 03 '26 00:04

rob3c