Skip to main content

Pulumi - API Gateway

Resources

📘 Pulumi Docs > AWS API Gateway

📘 Pulumi Docs > aws.apigateway

📘 Pulumi Docs > aws.apigatewayv2

V1 vs V2

Just a quick point on the two different versions of the aws.apigateway package. These are sort of poorly named, because v2 is not a newer updated version. They are intended to handle different things. V1 is for REST api's and V2 is for WebSocket and HTTP APIs.

Example

Creating an API endpoint and a Lambda function to handle events:

import * as apigateway from '@pulumi/aws-apigateway';
import * as aws from '@pulumi/aws';

const f = new aws.lambda.CallbackFunction('f', {
callback: async (ev, ctx) => {
console.log(JSON.stringify(ev));
return {
statusCode: 200,
body: 'goodbye',
};
},
});

const api = new apigateway.RestAPI('api', {
routes: [
{
path: '/',
method: 'GET',
eventHandler: f,
},
],
});

export const url = api.url;

Wow, that looks SO simple! Well that will create an API, but you won't have control over any of the options. To do that you need to do something more like this:

import * as pulumi from '@pulumi/pulumi';
import * as aws from '@pulumi/aws';
import {getSampleData} from './lambdas/get-sample-data/generate';
import {sampleDataDB} from './databases/sampleDataPostGreSQL';

// Get the current stack name
const stackName = pulumi.getStack();

// Create the API
const restApi = new aws.apigateway.RestApi('api', {
name: 'api',
});

// Create the Resource
const sampleDataResource = new aws.apigateway.Resource('sampleDataResource', {
restApi: restApi.id,
parentId: restApi.rootResourceId,
pathPart: 'sample-data',
});

// Create the GET Method
const getMethod = new aws.apigateway.Method('getSampleDataMethod', {
restApi: restApi.id,
resourceId: sampleDataResource.id,
httpMethod: 'GET',
authorization: 'NONE',
});

// Create the Lambda Integration with AWS type
const lambdaIntegration = new aws.apigateway.Integration(
'getSampleDataIntegration',
{
restApi: restApi.id,
resourceId: sampleDataResource.id,
httpMethod: getMethod.httpMethod,
type: 'AWS',
integrationHttpMethod: 'POST',
uri: getSampleData.invokeArn,
passthroughBehavior: 'WHEN_NO_MATCH',
requestTemplates: {
'application/json': JSON.stringify({
statusCode: 200,
}),
},
},
);

// Grant API Gateway permission to invoke the Lambda
const invokePermission = new aws.lambda.Permission(
'apiGatewayInvokePermission',
{
action: 'lambda:InvokeFunction',
function: getSampleData.name,
principal: 'apigateway.amazonaws.com',
sourceArn: pulumi.interpolate`${restApi.executionArn}/*/*`,
},
);

// Define Method Responses
const getMethodResponse = new aws.apigateway.MethodResponse(
'getMethodResponse',
{
restApi: restApi.id,
resourceId: sampleDataResource.id,
httpMethod: getMethod.httpMethod,
statusCode: '200',
responseModels: {
'application/json': 'Empty',
},
responseParameters: {
'method.response.header.Access-Control-Allow-Origin': true,
'method.response.header.Access-Control-Allow-Headers': true,
'method.response.header.Access-Control-Allow-Methods': true,
},
},
);

// Define Integration Responses
const getIntegrationResponse = new aws.apigateway.IntegrationResponse(
'getIntegrationResponse',
{
restApi: restApi.id,
resourceId: sampleDataResource.id,
httpMethod: getMethod.httpMethod,
statusCode: getMethodResponse.statusCode,
responseTemplates: {
'application/json': '',
},

// 😡 these response headers are simply not showing up in the responses despite documentation
// https://www.pulumi.com/registry/packages/aws/api-docs/apigateway/integrationresponse/
// however manually inserting the headers into the response in Lambda works...
// we shouldn't have to do this, but it's a workaround for now

responseParameters: {
'method.response.header.Access-Control-Allow-Origin': "'*'",
'method.response.header.Access-Control-Allow-Headers': "'Content-Type'",
'method.response.header.Access-Control-Allow-Methods': "'GET,OPTIONS'",
},
},
);

// Add an OPTIONS Method for Preflight Requests
const optionsMethod = new aws.apigateway.Method('optionsMethod', {
restApi: restApi.id,
resourceId: sampleDataResource.id,
httpMethod: 'OPTIONS',
authorization: 'NONE',
});

const optionsIntegration = new aws.apigateway.Integration(
'optionsIntegration',
{
restApi: restApi.id,
resourceId: sampleDataResource.id,
httpMethod: optionsMethod.httpMethod,
type: 'MOCK',
requestTemplates: {
'application/json': '{"statusCode": 200}',
},
},
);

const optionsMethodResponse = new aws.apigateway.MethodResponse(
'optionsMethodResponse',
{
restApi: restApi.id,
resourceId: sampleDataResource.id,
httpMethod: optionsMethod.httpMethod,
statusCode: '200',
responseParameters: {
'method.response.header.Access-Control-Allow-Origin': true,
'method.response.header.Access-Control-Allow-Headers': true,
'method.response.header.Access-Control-Allow-Methods': true,
},
},
);

const optionsIntegrationResponse = new aws.apigateway.IntegrationResponse(
'optionsIntegrationResponse',
{
restApi: restApi.id,
resourceId: sampleDataResource.id,
httpMethod: optionsMethod.httpMethod,
statusCode: optionsMethodResponse.statusCode,
responseParameters: {
'method.response.header.Access-Control-Allow-Origin': "'*'",
'method.response.header.Access-Control-Allow-Headers': "'Content-Type'",
'method.response.header.Access-Control-Allow-Methods': "'GET,OPTIONS'",
},
},
);

// Deploy the API
const deployment = new aws.apigateway.Deployment('apiDeployment', {
restApi: restApi.id,
triggers: {
redeployment: pulumi.concat(getMethod.id, optionsMethod.id),
},
});

// Create a Stage
const stage = new aws.apigateway.Stage('apiStage', {
restApi: restApi.id,
deployment: deployment.id,
stageName: stackName,
});

// Export the Invoke URL
export const url = pulumi.interpolate`${deployment.invokeUrl}${stage.stageName}/`;
export const dbEndpoint = sampleDataDB.endpoint;

Where you create the api and then manually generate all the resources, methods, integration responses etc etc.

Lambda Integration (CORS Error)

I found that if I was returning a response from Lambda, I simply could not get the integration response headers to allow cross origin requests (or show up at all, for the headers that I specified). So i'm not sure what's going on there, but I was able to work around this by manually setting the headers of the response in the Lambda function, which it appears to be just passing through instead of injecting and controlling them in API Gateway like it should be.

some-lambda-index.js
// ... the rest of the function

return {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Methods': 'GET,OPTIONS',
},
body: {},
};

Comments

Recent Work

Free desktop AI Chat client, designed for developers and businesses. Unlocks advanced model settings only available in the API. Includes quality of life features like custom syntax highlighting.

Learn More

BidBear

bidbear.io

Bidbear is a report automation tool. It downloads Amazon Seller and Advertising reports, daily, to a private database. It then merges and formats the data into beautiful, on demand, exportable performance reports.

Learn More