Amazon API Gateway
Amazon API Gateway is a fully managed service for creating, monitoring, and securing APIs at scale. It acts as a “front door” for REST and WebSocket applications that use backend services, and handles all the tasks necessary to accept and process up to hundreds of thousands of concurrent API calls, including traffic management, authorization and access control, monitoring, and API version management. API Gateway is inexpensive, has no minimum fees, and you only pay for the API calls you receive and the data transferred out.
Overview
Pulumi Crosswalk for Amazon Web Services (AWS) provides better AWS API management through significantly easier ways of programming an API Gateway. This includes using infrastructure as code techniques for simple, declarative APIs, including easy Lambda integration.
The examples below all use the AWS API Gateway component to configure API Gateway to serve several common scenarios. For more information about how to install and use this component, see its full documentation in the Pulumi Registry.
Creating and configuring routes
AWS API Gateway creates REST APIs that:
- Are HTTP based.
- Adhere to the REST protocol.
- Implement standard HTTP methods such as
GET
,POST
,PUT
,PATCH
, andDELETE
.
Each API Gateway instance defines a new API endpoint and a collection of API routes, each of which has a distinct URL.
Each API Gateway deployment is associated with a stage. A stage is a version of your API, such
as stage
, prod
, v1
, or v2
. For simple APIs, you will likely just have one. You can always define a custom
stage name, but if you leave it off, a default of stage
will be chosen.
API Gateway will auto-generate a domain name with built-in HTTPS support. The stage name will also be part of this URL. You’ll see later how to assign a custom domain, SSL certificate, and/or eliminate the stage name from the URL.
There are multiple ways to define APIs using Pulumi Crosswalk for AWS:
- Lambda Function Event Handler Route
- Static Route Served by S3
- Integration Route
- OpenAPI Specification for an Entire Endpoint
- OpenAPI Specification for a Single Route
Multiple endpoints on the same API Gateway can be defined using a combination of these techniques.
Handling requests with Lambda
An event-handler route is an API that will map to a Lambda function. You specify the path, HTTP method, and the Lambda function to invoke when the API is called. Pulumi offers multiple ways of defining the Lambda function and it provisions the appropriate permissions so that API Gateway can communicate with it.
This example creates an Amazon API Gateway REST API with a single API endpoint, listening at /
for GET
requests and a 200 OK
for each call.
"use strict";
const aws = require("@pulumi/aws");
const apigateway = require("@pulumi/aws-apigateway");
// A Lambda function to invoke.
const handler = new aws.lambda.CallbackFunction("handler", {
callback: async (event, context) => {
return {
statusCode: 200,
body: JSON.stringify({
message: "Hello from API Gateway!",
}),
};
},
});
// A REST API to route requests to the Lambda function.
const api = new apigateway.RestAPI("api", {
routes: [
{
path: "/",
method: "GET",
eventHandler: handler,
},
],
});
// The URL at which the REST API will be served.
exports.url = api.url;
import * as aws from "@pulumi/aws";
import * as apigateway from "@pulumi/aws-apigateway";
// A Lambda function to invoke.
const handler = new aws.lambda.CallbackFunction("handler", {
callback: async (event, context) => {
return {
statusCode: 200,
body: JSON.stringify({
message: "Hello from API Gateway!",
}),
};
},
});
// A REST API to route requests to the Lambda function.
const api = new apigateway.RestAPI("api", {
routes: [
{
path: "/",
method: "GET",
eventHandler: handler,
},
],
});
// The URL at which the REST API will be served.
export const url = api.url;
import json
import pulumi
import pulumi_aws as aws
import pulumi_aws_apigateway as apigateway
# An execution role to use for the Lambda function.
role = aws.iam.Role(
"role",
assume_role_policy=json.dumps(
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com",
},
}
],
}
),
managed_policy_arns=[aws.iam.ManagedPolicy.AWS_LAMBDA_BASIC_EXECUTION_ROLE],
)
# A Lambda function to invoke.
handler = aws.lambda_.Function(
"handler",
runtime="python3.9",
handler="handler.handler",
role=role.arn,
code=pulumi.FileArchive("./function"),
)
# A REST API to route requests to the Lambda function.
api = apigateway.RestAPI(
"api",
routes=[
apigateway.RouteArgs(
path="/", method=apigateway.Method.GET, event_handler=handler
)
],
)
# The URL at which the REST API will be served.
pulumi.export("url", api.url)
package main
import (
"encoding/json"
"github.com/pulumi/pulumi-aws-apigateway/sdk/v2/go/apigateway"
"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/iam"
"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/lambda"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
policy, err := json.Marshal(map[string]interface{}{
"Version": "2012-10-17",
"Statement": []map[string]interface{}{
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": map[string]interface{}{
"Service": "lambda.amazonaws.com",
},
},
},
})
if err != nil {
return err
}
// An execution role to use for the Lambda function.
role, err := iam.NewRole(ctx, "role", &iam.RoleArgs{
AssumeRolePolicy: pulumi.String(policy),
ManagedPolicyArns: pulumi.StringArray{
iam.ManagedPolicyAWSLambdaBasicExecutionRole,
},
})
if err != nil {
return err
}
// A Lambda function to invoke.
handler, err := lambda.NewFunction(ctx, "handler", &lambda.FunctionArgs{
Runtime: pulumi.String("python3.9"),
Handler: pulumi.String("handler.handler"),
Role: role.Arn,
Code: pulumi.NewFileArchive("./function"),
})
if err != nil {
return err
}
// A REST API to route requests to the Lambda function.
method := apigateway.MethodGET
api, err := apigateway.NewRestAPI(ctx, "api", &apigateway.RestAPIArgs{
Routes: []apigateway.RouteArgs{
{
Path: "/", Method: &method, EventHandler: handler,
},
},
})
if err != nil {
return err
}
// The URL at which the REST API will be served.
ctx.Export("url", api.Url)
return nil
})
}
using System.Collections.Generic;
using System.Text.Json;
using Pulumi;
using Aws = Pulumi.Aws;
using AwsApiGateway = Pulumi.AwsApiGateway;
return await Deployment.RunAsync(() =>
{
// An execution role to use for the Lambda function.
var role = new Aws.Iam.Role("role", new()
{
AssumeRolePolicy = JsonSerializer.Serialize(new Dictionary<string, object?>
{
["Version"] = "2012-10-17",
["Statement"] = new[]
{
new Dictionary<string, object?>
{
["Action"] = "sts:AssumeRole",
["Effect"] = "Allow",
["Principal"] = new Dictionary<string, object?>
{
["Service"] = "lambda.amazonaws.com",
},
},
},
}),
ManagedPolicyArns = new[]
{
Aws.Iam.ManagedPolicy.AWSLambdaBasicExecutionRole.ToString(),
},
});
// A Lambda function to invoke.
var handler = new Aws.Lambda.Function("handler", new()
{
Runtime = "python3.9",
Handler = "handler.handler",
Role = role.Arn,
Code = new FileArchive("./function"),
});
// A REST API to route requests to the Lambda function.
var api = new AwsApiGateway.RestAPI("api", new()
{
Routes =
{
new AwsApiGateway.Inputs.RouteArgs
{
Path = "/",
Method = AwsApiGateway.Method.GET,
EventHandler = handler,
},
},
});
return new Dictionary<string, object?>
{
// The URL at which the REST API will be served.
["url"] = api.Url,
};
});
package myproject;
import java.util.List;
import com.pulumi.Pulumi;
import com.pulumi.aws.iam.Role;
import com.pulumi.aws.iam.RoleArgs;
import com.pulumi.aws.iam.enums.ManagedPolicy;
import com.pulumi.awsapigateway.enums.Method;
import com.pulumi.aws.lambda.Function;
import com.pulumi.aws.lambda.FunctionArgs;
import com.pulumi.awsapigateway.RestAPI;
import com.pulumi.awsapigateway.RestAPIArgs;
import com.pulumi.awsapigateway.inputs.RouteArgs;
import static com.pulumi.codegen.internal.Serialization.*;
public class App {
public static void main(String[] args) {
Pulumi.run(ctx -> {
// An execution role to use for the Lambda function.
var role = new Role("role", RoleArgs.builder()
.assumeRolePolicy(serializeJson(
jsonObject(
jsonProperty("Version", "2012-10-17"),
jsonProperty("Statement", jsonArray(jsonObject(
jsonProperty("Action", "sts:AssumeRole"),
jsonProperty("Effect", "Allow"),
jsonProperty("Sid", ""),
jsonProperty("Principal", jsonObject(
jsonProperty("Service", "lambda.amazonaws.com")
))
)))
)))
.managedPolicyArns(List.of(ManagedPolicy.AWSLambdaBasicExecutionRole.getValue()))
.build());
// An execution role to use for the Lambda function.
var handler = new Function("handler", FunctionArgs.builder()
.runtime("python3.9")
.handler("handler.handler")
.role(role.arn())
.code(new com.pulumi.asset.FileArchive("./function"))
.build());
// An execution role to use for the Lambda function.
var api = new RestAPI("api", RestAPIArgs.builder()
.routes(new RouteArgs[]{
RouteArgs.builder()
.path("/")
.method(Method.GET)
.eventHandler(handler)
.build(),
})
.build());
// An execution role to use for the Lambda function.
ctx.export("url", api.url());
});
}
}
name: awsx-apigateway-lambda-yaml
runtime: yaml
description: An example that deploys an API Gateway REST API with a single endpoint backed by AWS Lambda.
resources:
# The URL at which the REST API will be served.
role:
type: aws:iam:Role
properties:
assumeRolePolicy:
fn::toJSON:
Version: "2012-10-17"
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: lambda.amazonaws.com
managedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
# The URL at which the REST API will be served.
handler:
type: aws:lambda:Function
properties:
runtime: python3.9
handler: handler.handler
role: ${role.arn}
code:
fn::fileArchive: ./function
# The URL at which the REST API will be served.
api:
type: aws-apigateway:RestAPI
properties:
routes:
- path: /
method: GET
eventHandler: ${handler}
outputs:
# The URL at which the REST API will be served.
url: ${api.url}
Route paths can also be parameterized to allow for matching specific patterns:
- A literal pattern like
/pets
will only match/pets
- A parameterized pattern like
/pets/{petId}
will match child routes such as/pet/123
- A wildcard pattern specified with
{proxy+}
like/pets/{proxy+}
will match all descendant paths such as/pets/tagged/cuddly
Running pulumi up
provisions the API Gateway and its routes and returns the URL:
$ pulumi up -y
Updating (dev)
Type Name Status
+ pulumi:pulumi:Stack awsx-apigateway-lambda-dev created (25s)
+ ├─ aws:iam:Role handler created (1s)
+ ├─ aws:lambda:Function handler created (14s)
+ ├─ aws:iam:RolePolicyAttachment handler-d32a66fa created (0.82s)
+ ├─ aws:iam:RolePolicyAttachment handler-019020e7 created (1s)
+ ├─ aws:iam:RolePolicyAttachment handler-1b4caae3 created (1s)
+ ├─ aws:iam:RolePolicyAttachment handler-7cd09230 created (1s)
+ ├─ aws:iam:RolePolicyAttachment handler-b5aeb6b6 created (1s)
+ ├─ aws:iam:RolePolicyAttachment handler-4aaabb8e created (1s)
+ ├─ aws:iam:RolePolicyAttachment handler-e1a3786d created (1s)
+ ├─ aws:iam:RolePolicyAttachment handler-74d12784 created (1s)
+ ├─ aws:iam:RolePolicyAttachment handler-a1de8170 created (1s)
+ └─ aws-apigateway:index:RestAPI api created (6s)
+ ├─ aws:apigateway:RestApi api created (2s)
+ ├─ aws:apigateway:Deployment api created (0.42s)
+ ├─ aws:lambda:Permission api-fa520765 created (0.48s)
+ └─ aws:apigateway:Stage api created (0.54s)
Outputs:
url: "https://a5yj5n7rz0.execute-api.us-west-2.amazonaws.com/stage/"
Resources:
+ 17 created
Duration: 26s
You can curl
the exported URL to see that it’s up and running:
$ curl $(pulumi stack output url)
{"message":"Hello from API Gateway!"}%
For more complete information about creating Lambda Functions, see the Pulumi Crosswalk for AWS Lambda documentation.
Serving static files from S3
A Static Route serves static content from S3 at an API endpoint.
With the API Gateway component, you specify a local path (either a file or an entire directory), and the component manages the creation of the S3 bucket and the synchronization of the files to S3 objects.
Given a directory www
containing an index.html
file, the following program creates an API Gateway API that serves the file at the root path (/
) of the stage endpoint:
"use strict";
const apigateway = require("@pulumi/aws-apigateway");
const api = new apigateway.RestAPI("api", {
routes: [
{
path: "/",
localPath: "www",
},
],
});
exports.url = api.url;
import * as apigateway from "@pulumi/aws-apigateway";
const api = new apigateway.RestAPI("api", {
routes: [
{
path: "/",
localPath: "www",
},
],
});
export const url = api.url;
import pulumi
import pulumi_aws as aws
import pulumi_aws_apigateway as apigateway
api = apigateway.RestAPI(
"api",
routes=[
apigateway.RouteArgs(
path="/",
method=apigateway.Method.GET,
local_path="www",
)
],
)
pulumi.export("url", api.url)
package main
import (
"github.com/pulumi/pulumi-aws-apigateway/sdk/v2/go/apigateway"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
api, err := apigateway.NewRestAPI(ctx, "api", &apigateway.RestAPIArgs{
Routes: []apigateway.RouteArgs{
{
Path: "/",
LocalPath: pulumi.StringRef("www"),
},
},
})
if err != nil {
return err
}
ctx.Export("url", api.Url)
return nil
})
}
using System.Collections.Generic;
using Pulumi;
using AwsApiGateway = Pulumi.AwsApiGateway;
return await Deployment.RunAsync(() =>
{
var api = new AwsApiGateway.RestAPI("api", new()
{
Routes =
{
new AwsApiGateway.Inputs.RouteArgs
{
Path = "/",
LocalPath = "www",
},
},
});
return new Dictionary<string, object?>
{
["url"] = api.Url,
};
});
package myproject;
import com.pulumi.Pulumi;
import com.pulumi.awsapigateway.enums.Method;
import com.pulumi.awsapigateway.RestAPI;
import com.pulumi.awsapigateway.RestAPIArgs;
import com.pulumi.awsapigateway.inputs.RouteArgs;
public class App {
public static void main(String[] args) {
Pulumi.run(ctx -> {
var api = new RestAPI("api", RestAPIArgs.builder()
.routes(new RouteArgs[]{
RouteArgs.builder()
.path("/")
.method(Method.GET)
.localPath("www")
.build(),
})
.build());
ctx.export("url", api.url());
});
}
}
name: awsx-apigateway-s3-yaml
runtime: yaml
description: An example that uses API Gateway to serve static content from Amazon S3.
resources:
api:
type: aws-apigateway:RestAPI
properties:
routes:
- path: /
localPath: www
outputs:
url: ${api.url}
Again, after running pulumi up
, you can curl
the exported URL:
$ curl $(pulumi stack output url)
<html>
<body>
<p>Hello from API Gateway + S3!</p>
</body>
</html>
By default, any index documents will be automatically served by S3 when directories are retrieved over HTTP.
(See Configuring an Index Document in the AWS documentation for more information.) To suppress this behavior in the static route, set the index
property to false
. To use a different default document name, set index
property to the name of the file you’d like to use, such as default.html
.
If the local path points to a directory, the route will automatically be created as a proxy path (i.e. /{proxy+}
) to match
all sub-directories and the content type for all files will be inferred automatically. If the local path points to a single
file, you can specify the content type explicitly using the contentType
property.
Integrating with other AWS services
If neither of the above route types work for you, Amazon API Gateway integrations connect an API Gateway endpoint to backend services that will execute code in response to requests. The previous Lambda and S3 examples use API Gateway integrations internally, even if it’s not evident in the simple interface exposed. Integrations give you full control over how HTTP requests are handled, and responses served, by an API Gateway route.
If you want more flexibility than the earlier methods — e.g., to proxy HTTP requests, integrate with other AWS services, or mock your APIs — you can create an integration route by specifying the target
property on your route.
An integration route is a route that maps an endpoint to a specified backend. Supported integration route types include:
aws
: Allows an API to expose an AWS service action, such as invoking Amazon Lambda Functions, Amazon DynamoDB, Amazon Simple Notification Service, or Amazon Simple Queue Service. You must configure the data mappings between the HTTP and underlying AWS service requests and responses.aws_proxy
: Also allows an API expose an AWS service action, but instead passes the HTTP request (including headers, path, query parameters, and body) directly to the underlying action.http
: Allows an API to expose HTTP endpoints with custom integration requests and responses. You must configure the data mappings between the HTTP and underlying AWS service requests and responses.http_proxy
: Allows an API to expose HTTP endpoints with a streamlined integration, without requiring you to configure the custom data mappings as with thehttp
type.mock
: Allows API Gateway to return a response without sending a request to the backend. This is useful for API testing without needing to configure any backend to handle requests.
The following example sets up an http_proxy
integration that passes requests directly to another endpoint, in this case https://www.example.com
:
"use strict";
const apigateway = require("@pulumi/aws-apigateway");
const api = new apigateway.RestAPI("api", {
routes: [
{
path: "/",
target: {
type: "http_proxy",
uri: "https://www.example.com/",
},
},
],
});
exports.url = api.url;
import * as apigateway from "@pulumi/aws-apigateway";
const api = new apigateway.RestAPI("api", {
routes: [
{
path: "/",
target: {
type: "http_proxy",
uri: "https://www.example.com/",
},
},
],
});
export const url = api.url;
import pulumi
import pulumi_aws_apigateway as apigateway
api = apigateway.RestAPI(
"api",
routes=[
apigateway.RouteArgs(
path="/",
method=apigateway.Method.GET,
target=apigateway.TargetArgs(
type="http_proxy",
uri="https://www.example.com/",
),
),
],
)
pulumi.export("url", api.url)
package main
import (
"github.com/pulumi/pulumi-aws-apigateway/sdk/v2/go/apigateway"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
api, err := apigateway.NewRestAPI(ctx, "api", &apigateway.RestAPIArgs{
Routes: []apigateway.RouteArgs{
{
Path: "/",
Target: &apigateway.TargetArgs{
Type: apigateway.IntegrationType_Http_proxy,
Uri: pulumi.String("https://www.example.com/"),
},
},
},
})
if err != nil {
return err
}
ctx.Export("url", api.Url)
return nil
})
}
using System.Collections.Generic;
using Pulumi;
using AwsApiGateway = Pulumi.AwsApiGateway;
return await Deployment.RunAsync(() =>
{
var api = new AwsApiGateway.RestAPI("api", new()
{
Routes =
{
new AwsApiGateway.Inputs.RouteArgs
{
Path = "/",
Target = new AwsApiGateway.Inputs.TargetArgs()
{
Type = AwsApiGateway.IntegrationType.Http_proxy,
Uri = "https://www.example.com/",
},
},
},
});
return new Dictionary<string, object?>
{
["url"] = api.Url,
};
});
package myproject;
import com.pulumi.Pulumi;
import com.pulumi.awsapigateway.enums.IntegrationType;
import com.pulumi.awsapigateway.enums.Method;
import com.pulumi.awsapigateway.RestAPI;
import com.pulumi.awsapigateway.RestAPIArgs;
import com.pulumi.awsapigateway.inputs.RouteArgs;
import com.pulumi.awsapigateway.inputs.TargetArgs;
public class App {
public static void main(String[] args) {
Pulumi.run(ctx -> {
var api = new RestAPI("api", RestAPIArgs.builder()
.routes(new RouteArgs[]{
RouteArgs.builder()
.path("/")
.method(Method.GET)
.target(TargetArgs.builder()
.type(IntegrationType.Http_proxy)
.uri("https://www.example.com/")
.build()
)
.build(),
})
.build());
ctx.export("url", api.url());
});
}
}
name: awsx-apigateway-http-proxy-yaml
runtime: yaml
description: An example that creates an API Gateway instance and configures a route as an HTTP proxy.
resources:
api:
type: aws-apigateway:RestAPI
properties:
routes:
- path: /
method: GET
target:
type: http_proxy
uri: https://www.example.com/
outputs:
url: ${api.url}
Controlling Access to APIs
AWS API Gateway supports several mechanisms for controlling and managing access to your APIs, including authentication and authorization with resource policies, standard AWS IAM roles and policies, Cognito user pools, and Lambda authorizers. You can also configure access in other ways, such as cross-origin resource sharing (CORS), client-side SSL certificates, Amazon Web Application Firewall (WAF), and limiting access to authorized clients through usage plans and API keys.
The API Gateway component’s RestAPI
resource supports three methods of controlling access to your APIs:
Amazon Cognito user pools let you create customizable authentication and authorization for your APIs.
Lambda authorizers are Lambda functions that control access to your APIs based on HTTP information available in headers, paths, query strings, or other request information, including bearer tokens.
Usage plans let you provide API keys to customers, and then track and limit usage of your APIs.
Details on each are below. For those not directly supported, all of these capabilities are accessible to you in the Pulumi AWS package, and are described in depth in the article Controlling and Managing Access to a REST API in API Gateway.
Cognito Authorizers
Cognito Authorizers allow you to use Amazon Cognito User Pools as an authorizer for API Gateway. With a user pool, your users can sign into your web or mobile app through Amazon Cognito directly, social identity providers like Facebook, or SAML identity providers like Google. This enables your API Gateway to offload the difficult work of security to Cognito entirely.
To require users to sign in through Cognito, you must specify the source of the authorization token (normally the Authorization
header) and the ARN of the Cognito User Pool:
"use strict";
const aws = require("@pulumi/aws");
const apigateway = require("@pulumi/aws-apigateway");
const userPool = new aws.cognito.UserPool("user-pool");
const api = new apigateway.RestAPI("api", {
routes: [
{
path: "/",
localPath: "www",
authorizers: [
{
parameterName: "Authorization",
identitySource: ["method.request.header.Authorization"],
providerARNs: [userPool.arn],
},
],
},
],
});
exports.url = api.url;
import * as aws from "@pulumi/aws";
import * as apigateway from "@pulumi/aws-apigateway";
const userPool = new aws.cognito.UserPool("user-pool");
const api = new apigateway.RestAPI("api", {
routes: [
{
path: "/",
localPath: "www",
authorizers: [
{
parameterName: "Authorization",
identitySource: ["method.request.header.Authorization"],
providerARNs: [userPool.arn],
},
],
},
],
});
export const url = api.url;
import pulumi
import pulumi_aws as aws
import pulumi_aws_apigateway as apigateway
user_pool = aws.cognito.UserPool("user-pool")
api = apigateway.RestAPI(
"api",
routes=[
apigateway.RouteArgs(
path="/",
method=apigateway.Method.GET,
local_path="www",
authorizers=apigateway.AuthorizerArgs(
parameter_name="Authorization",
identity_source=["method.request.header.Authorization"],
provider_arns=[user_pool.arn],
),
)
],
)
pulumi.export("url", api.url)
package main
import (
"github.com/pulumi/pulumi-aws-apigateway/sdk/v2/go/apigateway"
"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/cognito"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
userPool, err := cognito.NewUserPool(ctx, "user-pool", nil)
if err != nil {
return err
}
api, err := apigateway.NewRestAPI(ctx, "api", &apigateway.RestAPIArgs{
Routes: []apigateway.RouteArgs{
{
Path: "/",
LocalPath: pulumi.StringRef("www"),
Authorizers: []apigateway.AuthorizerArgs{
{
ParameterName: "Authorization",
IdentitySource: []string{"method.request.header.Authorization"},
ProviderARNs: []pulumi.StringInput{
userPool.Arn,
},
},
},
},
},
})
if err != nil {
return err
}
ctx.Export("url", api.Url)
return nil
})
}
using System.Collections.Generic;
using Pulumi;
using Aws = Pulumi.Aws;
using AwsApiGateway = Pulumi.AwsApiGateway;
return await Deployment.RunAsync(() =>
{
var userPool = new Aws.Cognito.UserPool("user-pool");
var api = new AwsApiGateway.RestAPI(
"api",
new()
{
Routes =
{
new AwsApiGateway.Inputs.RouteArgs
{
Path = "/",
Method = AwsApiGateway.Method.GET,
LocalPath = "www",
Authorizers =
{
new AwsApiGateway.Inputs.AuthorizerArgs
{
ParameterName = "Authorization",
IdentitySource = { "method.request.header.Authorization" },
ProviderARNs = { userPool.Arn },
},
},
},
},
}
);
return new Dictionary<string, object?> { ["url"] = api.Url };
});
package myproject;
import java.util.List;
import com.pulumi.Pulumi;
import com.pulumi.aws.cognito.UserPool;
import com.pulumi.awsapigateway.enums.Method;
import com.pulumi.awsapigateway.RestAPI;
import com.pulumi.awsapigateway.RestAPIArgs;
import com.pulumi.awsapigateway.inputs.AuthorizerArgs;
import com.pulumi.awsapigateway.inputs.RouteArgs;
import com.pulumi.core.Output;
public class App {
public static void main(String[] args) {
Pulumi.run(ctx -> {
var userPool = new UserPool("user-pool");
var api = new RestAPI("api", RestAPIArgs.builder()
.routes(new RouteArgs[]{
RouteArgs.builder()
.path("/")
.method(Method.GET)
.localPath("www")
.authorizers(new AuthorizerArgs[]{
AuthorizerArgs.builder()
.parameterName("Authorization")
.identitySource(List.of("method.request.header.Authorization"))
.providerARNs(List.of(userPool.arn()))
.build(),
})
.build(),
})
.build());
ctx.export("url", api.url());
});
}
}
name: awsx-apigateway-auth-cognito-yaml
runtime: yaml
description: An example that deploys an API Gateway REST API with an Amazon Cognito authorizer.
resources:
userPool:
type: aws:cognito:UserPool
api:
type: aws-apigateway:RestAPI
properties:
routes:
- path: /
localPath: www
authorizers:
- parameterName: Authorization
identitySource:
- method.request.header.Authorization
providerARNs:
- ${userPool.arn}
outputs:
url: ${api.url}
When deployed, this configuration will require that a user authenticate, obtain an identity/access token, and call your API with said token.
Lambda Authorizers
Lambda Authorizers are AWS Lambda Functions that control access to an API. This allows you to use information in the request itself, including headers, paths, query parameters, or tokens, to decide whether a request is authorized to hit the backend.
You can define a Lambda Authorizer for an event-handler route or a static route. API Gateway supports request
or token
type Lambda authorizers:
Request authorizers can use any part of the request parameters (e.g., headers, path parameters, or query parameters).
Token authorizers use an authorization token (i.e., a header of the form
Authorization: token <token>
)
To define an Authorizer, you provide a Lambda that receives an authorizer event and responds with a valid authorizer response. See Pulumi Crosswalk for AWS Lambda for other ways you can define your Lambda for the Authorizer.
Below is an example of a custom request
authorizer. Because the authorizer has access to the content of the HTTP request, it can use any of the request’s properties to determine whether to grant access to the resource requested. For demonstration, this authorizer validates the request using a single, hard-coded token. (In practice, you’d more likely have the authorizer query a database or contact another service for this purpose.)
"use strict";
const aws = require("@pulumi/aws");
const apigateway = require("@pulumi/aws-apigateway");
const authorizer = new aws.lambda.CallbackFunction("authorizer", {
callback: async (event, context) => {
const effect = event.headers.Authorization === "token a-good-token" ? "Allow" : "Deny";
return {
principalId: "my-user",
policyDocument: {
Version: "2012-10-17",
Statement: [
{
Action: "execute-api:Invoke",
Effect: effect,
Resource: event.methodArn,
},
],
},
};
},
});
const api = new apigateway.RestAPI("api", {
routes: [
{
path: "/",
method: "GET",
localPath: "www",
authorizers: [
{
authType: "custom",
parameterName: "Authorization",
type: "request",
identitySource: ["method.request.header.Authorization"],
handler: authorizer,
},
],
},
],
});
exports.url = api.url;
import * as aws from "@pulumi/aws";
import * as apigateway from "@pulumi/aws-apigateway";
const authorizer = new aws.lambda.CallbackFunction("authorizer", {
callback: async (event: any, context) => {
const effect = event.headers.Authorization === "token a-good-token" ? "Allow" : "Deny";
return {
principalId: "my-user",
policyDocument: {
Version: "2012-10-17",
Statement: [
{
Action: "execute-api:Invoke",
Effect: effect,
Resource: event.methodArn,
},
],
},
};
},
});
const api = new apigateway.RestAPI("api", {
routes: [
{
path: "/",
method: "GET",
localPath: "www",
authorizers: [
{
authType: "custom",
parameterName: "Authorization",
type: "request",
identitySource: ["method.request.header.Authorization"],
handler: authorizer,
},
],
},
],
});
export const url = api.url;
import json
import pulumi
import pulumi_aws as aws
import pulumi_aws_apigateway as apigateway
role = aws.iam.Role(
"role",
assume_role_policy=json.dumps(
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com",
},
}
],
}
),
managed_policy_arns=[aws.iam.ManagedPolicy.AWS_LAMBDA_BASIC_EXECUTION_ROLE],
)
authorizer = aws.lambda_.Function(
"authorizer",
runtime="python3.9",
handler="handler.handler",
role=role.arn,
code=pulumi.FileArchive("./authorizer"),
)
api = apigateway.RestAPI(
"api",
routes=[
apigateway.RouteArgs(
path="/",
method=apigateway.Method.GET,
local_path="www",
authorizers=[
apigateway.AuthorizerArgs(
auth_type="custom",
type="request",
parameter_name="Authorization",
identity_source=["method.request.header.Authorization"],
handler=authorizer,
),
],
),
],
)
pulumi.export("url", api.url)
package main
import (
"encoding/json"
"github.com/pulumi/pulumi-aws-apigateway/sdk/v2/go/apigateway"
"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/iam"
"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/lambda"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
policy, err := json.Marshal(map[string]interface{}{
"Version": "2012-10-17",
"Statement": []map[string]interface{}{
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": map[string]interface{}{
"Service": "lambda.amazonaws.com",
},
},
},
})
if err != nil {
return err
}
role, err := iam.NewRole(ctx, "role", &iam.RoleArgs{
AssumeRolePolicy: pulumi.String(policy),
ManagedPolicyArns: pulumi.StringArray{
iam.ManagedPolicyAWSLambdaBasicExecutionRole,
},
})
if err != nil {
return err
}
authorizer, err := lambda.NewFunction(ctx, "authorizer", &lambda.FunctionArgs{
Runtime: pulumi.String("python3.9"),
Handler: pulumi.String("handler.handler"),
Role: role.Arn,
Code: pulumi.NewFileArchive("./authorizer"),
})
if err != nil {
return err
}
methodGET := apigateway.MethodGET
api, err := apigateway.NewRestAPI(ctx, "api", &apigateway.RestAPIArgs{
Routes: []apigateway.RouteArgs{
{
Path: "/",
Method: &methodGET,
LocalPath: pulumi.StringRef("www"),
Authorizers: []apigateway.AuthorizerArgs{
{
AuthType: pulumi.StringRef("custom"),
Type: pulumi.StringRef("request"),
ParameterName: "Authorization",
IdentitySource: []string{"method.request.header.Authorization"},
Handler: authorizer,
},
},
},
},
})
if err != nil {
return err
}
ctx.Export("url", api.Url)
return nil
})
}
using System.Collections.Generic;
using System.Text.Json;
using Pulumi;
using Aws = Pulumi.Aws;
using AwsApiGateway = Pulumi.AwsApiGateway;
return await Deployment.RunAsync(() =>
{
var role = new Aws.Iam.Role("role", new()
{
AssumeRolePolicy = JsonSerializer.Serialize(new Dictionary<string, object?>
{
["Version"] = "2012-10-17",
["Statement"] = new[]
{
new Dictionary<string, object?>
{
["Action"] = "sts:AssumeRole",
["Effect"] = "Allow",
["Principal"] = new Dictionary<string, object?>
{
["Service"] = "lambda.amazonaws.com",
},
},
},
}),
ManagedPolicyArns = new[]
{
Aws.Iam.ManagedPolicy.AWSLambdaBasicExecutionRole.ToString(),
},
});
var authorizer = new Aws.Lambda.Function("authorizer", new()
{
Runtime = "python3.9",
Handler = "handler.handler",
Role = role.Arn,
Code = new FileArchive("./authorizer"),
});
var api = new AwsApiGateway.RestAPI(
"api",
new()
{
Routes =
{
new AwsApiGateway.Inputs.RouteArgs
{
Path = "/",
Method = AwsApiGateway.Method.GET,
LocalPath = "www",
Authorizers =
{
new AwsApiGateway.Inputs.AuthorizerArgs
{
AuthType = "custom",
Type = "request",
ParameterName = "Authorization",
IdentitySource = { "method.request.header.Authorization" },
Handler = authorizer,
},
},
},
},
}
);
return new Dictionary<string, object?> { ["url"] = api.Url };
});
package myproject;
import java.util.List;
import com.pulumi.Pulumi;
import com.pulumi.aws.iam.Role;
import com.pulumi.aws.iam.RoleArgs;
import com.pulumi.aws.iam.enums.ManagedPolicy;
import com.pulumi.awsapigateway.enums.Method;
import com.pulumi.aws.lambda.Function;
import com.pulumi.aws.lambda.FunctionArgs;
import com.pulumi.awsapigateway.RestAPI;
import com.pulumi.awsapigateway.RestAPIArgs;
import com.pulumi.awsapigateway.inputs.AuthorizerArgs;
import com.pulumi.awsapigateway.inputs.RouteArgs;
import static com.pulumi.codegen.internal.Serialization.*;
public class App {
public static void main(String[] args) {
Pulumi.run(ctx -> {
var role = new Role("role", RoleArgs.builder()
.assumeRolePolicy(serializeJson(
jsonObject(
jsonProperty("Version", "2012-10-17"),
jsonProperty("Statement", jsonArray(jsonObject(
jsonProperty("Action", "sts:AssumeRole"),
jsonProperty("Effect", "Allow"),
jsonProperty("Sid", ""),
jsonProperty("Principal", jsonObject(
jsonProperty("Service", "lambda.amazonaws.com")
))
)))
)))
.managedPolicyArns(List.of(ManagedPolicy.AWSLambdaBasicExecutionRole.getValue()))
.build());
var authorizer = new Function("authorizer", FunctionArgs.builder()
.runtime("python3.9")
.handler("handler.handler")
.role(role.arn())
.code(new com.pulumi.asset.FileArchive("./authorizer"))
.build());
var api = new RestAPI("api", RestAPIArgs.builder()
.routes(new RouteArgs[]{
RouteArgs.builder()
.path("/")
.method(Method.GET)
.localPath("www")
.authorizers(new AuthorizerArgs[]{
AuthorizerArgs.builder()
.authType("custom")
.type("request")
.parameterName("Authorization")
.identitySource(List.of("method.request.header.Authorization"))
.handler(authorizer)
.build(),
})
.build(),
})
.build());
ctx.export("url", api.url());
});
}
}
name: awsx-apigateway-auth-lambda-yaml
runtime: yaml
description: An example that deploys an API Gateway REST API with an AWS Lambda authorizer.
resources:
role:
type: aws:iam:Role
properties:
assumeRolePolicy:
fn::toJSON:
Version: "2012-10-17"
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: lambda.amazonaws.com
managedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
authorizer:
type: aws:lambda:Function
properties:
runtime: python3.9
handler: handler.handler
role: ${role.arn}
code:
fn::fileArchive: ./authorizer
api:
type: aws-apigateway:RestAPI
properties:
routes:
- path: /
localPath: www
authorizers:
- authType: custom
type: request
parameterName: Authorization
identitySource:
- method.request.header.Authorization
handler: ${authorizer}
outputs:
url: ${api.url}
Once deployed, the protected route only accepts requests containing the token in the Authorization
header:
$ curl -H "Authorization: token a-good-token" $(pulumi stack output url)
<html>
<body>
<p>Hello from API Gateway + S3!</p>
</body>
</html>
$ curl $(pulumi stack output url)
{"message":"Unauthorized"}
For additional information about API Gateway Lambda Authorizers, see the AWS documentation.
Request Validation
API Gateway can perform basic validations against the parameters of a request, the request payload, or both. When a validation fails, a 400 error is returned immediately, without invoking the backend integration, and the validation results are published to CloudWatch Logs, eliminating unnecessary calls to the backend.
For basic validation, API Gateway can verify either or both of these conditions:
The request parameters of an incoming request (headers, query parameters, and path parameters) are included and non-blank.
The request payload adheres to the configured JSON schema request model of the method.
When enabling validation, you choose a validation scope and validation rules.
Assigning Validators to APIs and Methods
Validators can be assigned for an entire API or at the individual method level, such as only for POST
requests on a given
route. The validators defined at a method level override any validator set at the global (i.e., API Gateway instance) level.
To enable validation, pass the requestValidator
property either on the RestAPI
resource itself or one or more of its individual routes. The following validator values are supported:
ALL
: Validates both the request body and request parameters.BODY_ONLY
: Validates only the request body.PARAMS_ONLY
: Validates only the request parameters.
The following example enables parameter validation on all routes, and both parameter and body validation on a specific route:
"use strict";
const apigateway = require("@pulumi/aws-apigateway");
const api = new apigateway.RestAPI("api", {
requestValidator: apigateway.RequestValidator.PARAMS_ONLY,
routes: [
{
path: "/search",
method: apigateway.Method.GET,
target: {
type: "http_proxy",
uri: "https://www.example.com/",
},
requestValidator: apigateway.RequestValidator.ALL,
requiredParameters: [
{
name: "q",
in: "query",
},
],
},
],
});
exports.url = api.url;
import * as apigateway from "@pulumi/aws-apigateway";
const api = new apigateway.RestAPI("api", {
requestValidator: apigateway.RequestValidator.PARAMS_ONLY,
routes: [
{
path: "/search",
method: apigateway.Method.GET,
target: {
type: "http_proxy",
uri: "https://www.example.com/",
},
requestValidator: apigateway.RequestValidator.ALL,
requiredParameters: [
{
name: "q",
in: "query",
},
],
},
],
});
export const url = api.url;
import pulumi
import pulumi_aws_apigateway as apigateway
api = apigateway.RestAPI(
"api",
apigateway.RestAPIArgs(
request_validator=apigateway.RequestValidator.PARAM_S_ONLY,
routes=[
apigateway.RouteArgs(
path="/search",
method=apigateway.Method.GET,
target=apigateway.TargetArgs(
type="http_proxy",
uri="https://www.example.com/",
),
request_validator=apigateway.RequestValidator.ALL,
required_parameters=[
apigateway.RequiredParameterArgs(
name="q",
in_="query",
),
],
),
],
)
)
pulumi.export("url", api.url)
package main
import (
"github.com/pulumi/pulumi-aws-apigateway/sdk/v2/go/apigateway"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
GET := apigateway.MethodGET
ALL := apigateway.RequestValidatorALL
PARAMS_ONLY := apigateway.RequestValidator_PARAMS_ONLY
api, err := apigateway.NewRestAPI(ctx, "api", &apigateway.RestAPIArgs{
RequestValidator: &PARAMS_ONLY,
Routes: []apigateway.RouteArgs{
{
Path: "/search",
Method: &GET,
Target: &apigateway.TargetArgs{
Type: apigateway.IntegrationType_Http_proxy,
Uri: pulumi.String("https://www.example.com/"),
},
RequestValidator: &ALL,
RequiredParameters: []apigateway.RequiredParameterArgs{
{
Name: pulumi.StringPtr("q"),
In: pulumi.StringPtr("query"),
},
},
},
},
})
if err != nil {
return err
}
ctx.Export("url", api.Url)
return nil
})
}
using System.Collections.Generic;
using Pulumi;
using Pulumi.AwsApiGateway.Inputs;
using AwsApiGateway = Pulumi.AwsApiGateway;
return await Deployment.RunAsync(() =>
{
var api = new AwsApiGateway.RestAPI("api", new()
{
RequestValidator = AwsApiGateway.RequestValidator.PARAMS_ONLY,
Routes = {
new RouteArgs()
{
Path = "/search",
Method = AwsApiGateway.Method.GET,
Target = new TargetArgs()
{
Type = AwsApiGateway.IntegrationType.Http_proxy,
Uri = "https://www.example.com/",
},
RequestValidator = AwsApiGateway.RequestValidator.ALL,
RequiredParameters = {
new RequiredParameterArgs() {
Name = "q",
In = "query",
},
},
},
},
});
return new Dictionary<string, object?>
{
["url"] = api.Url,
};
});
package myproject;
import java.util.List;
import com.pulumi.Pulumi;
import com.pulumi.awsapigateway.enums.IntegrationType;
import com.pulumi.awsapigateway.enums.Method;
import com.pulumi.awsapigateway.enums.RequestValidator;
import com.pulumi.awsapigateway.RestAPI;
import com.pulumi.awsapigateway.RestAPIArgs;
import com.pulumi.awsapigateway.inputs.RequiredParameterArgs;
import com.pulumi.awsapigateway.inputs.RouteArgs;
import com.pulumi.awsapigateway.inputs.TargetArgs;
public class App {
public static void main(String[] args) {
Pulumi.run(ctx -> {
var api = new RestAPI("api", RestAPIArgs.builder()
.requestValidator(RequestValidator.PARAMS_ONLY)
.routes(new RouteArgs[]{
RouteArgs.builder()
.path("/search")
.method(Method.GET)
.target(TargetArgs.builder()
.type(IntegrationType.Http_proxy)
.uri("https://www.example.com/")
.build()
)
.requestValidator(RequestValidator.ALL)
.requiredParameters(List.of(RequiredParameterArgs.builder()
.name("q")
.in("query")
.build()))
.build(),
})
.build());
ctx.export("url", api.url());
});
}
}
name: awsx-apigateway-validation-types-yaml
runtime: yaml
description: An example of configuring request validators for an API Gateway instance.
resources:
api:
type: aws-apigateway:RestAPI
properties:
requestValidator: PARAMS_ONLY
routes:
- path: /search
method: GET
target:
type: http_proxy
uri: https://www.example.com/
requestValidator: ALL
requiredParameters:
- name: q
in: query
outputs:
url: ${api.url}
Request Parameter Validation
To validate that a particular parameter is present in each request, use the requiredParams
route property. This property is an array that defines each required parameter and where the parameter is expected to be found (header
, path
, query
), using the name
and in
properties, respectively.
The following program uses request validation to ensure that the q
parameter is present and non-empty on all /search
requests:
"use strict";
const apigateway = require("@pulumi/aws-apigateway");
const api = new apigateway.RestAPI("api", {
requestValidator: apigateway.RequestValidator.PARAMS_ONLY,
routes: [
{
path: "/search",
method: apigateway.Method.GET,
target: {
type: "http_proxy",
uri: "https://www.example.com/",
},
requestValidator: apigateway.RequestValidator.ALL,
requiredParameters: [
{
name: "q",
in: "query",
},
],
},
],
});
exports.url = api.url;
import * as apigateway from "@pulumi/aws-apigateway";
const api = new apigateway.RestAPI("api", {
requestValidator: apigateway.RequestValidator.PARAMS_ONLY,
routes: [
{
path: "/search",
method: apigateway.Method.GET,
target: {
type: "http_proxy",
uri: "https://www.example.com/",
},
requestValidator: apigateway.RequestValidator.ALL,
requiredParameters: [
{
name: "q",
in: "query",
},
],
},
],
});
export const url = api.url;
import pulumi
import pulumi_aws_apigateway as apigateway
api = apigateway.RestAPI(
"api",
apigateway.RestAPIArgs(
request_validator=apigateway.RequestValidator.PARAM_S_ONLY,
routes=[
apigateway.RouteArgs(
path="/search",
method=apigateway.Method.GET,
target=apigateway.TargetArgs(
type="http_proxy",
uri="https://www.example.com/",
),
request_validator=apigateway.RequestValidator.ALL,
required_parameters=[
apigateway.RequiredParameterArgs(
name="q",
in_="query",
),
],
),
],
)
)
pulumi.export("url", api.url)
package main
import (
"github.com/pulumi/pulumi-aws-apigateway/sdk/v2/go/apigateway"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
GET := apigateway.MethodGET
ALL := apigateway.RequestValidatorALL
PARAMS_ONLY := apigateway.RequestValidator_PARAMS_ONLY
api, err := apigateway.NewRestAPI(ctx, "api", &apigateway.RestAPIArgs{
RequestValidator: &PARAMS_ONLY,
Routes: []apigateway.RouteArgs{
{
Path: "/search",
Method: &GET,
Target: &apigateway.TargetArgs{
Type: apigateway.IntegrationType_Http_proxy,
Uri: pulumi.String("https://www.example.com/"),
},
RequestValidator: &ALL,
RequiredParameters: []apigateway.RequiredParameterArgs{
{
Name: pulumi.StringPtr("q"),
In: pulumi.StringPtr("query"),
},
},
},
},
})
if err != nil {
return err
}
ctx.Export("url", api.Url)
return nil
})
}
using System.Collections.Generic;
using Pulumi;
using Pulumi.AwsApiGateway.Inputs;
using AwsApiGateway = Pulumi.AwsApiGateway;
return await Deployment.RunAsync(() =>
{
var api = new AwsApiGateway.RestAPI("api", new()
{
RequestValidator = AwsApiGateway.RequestValidator.PARAMS_ONLY,
Routes = {
new RouteArgs()
{
Path = "/search",
Method = AwsApiGateway.Method.GET,
Target = new TargetArgs()
{
Type = AwsApiGateway.IntegrationType.Http_proxy,
Uri = "https://www.example.com/",
},
RequestValidator = AwsApiGateway.RequestValidator.ALL,
RequiredParameters = {
new RequiredParameterArgs() {
Name = "q",
In = "query",
},
},
},
},
});
return new Dictionary<string, object?>
{
["url"] = api.Url,
};
});
package myproject;
import java.util.List;
import com.pulumi.Pulumi;
import com.pulumi.awsapigateway.enums.IntegrationType;
import com.pulumi.awsapigateway.enums.Method;
import com.pulumi.awsapigateway.enums.RequestValidator;
import com.pulumi.awsapigateway.RestAPI;
import com.pulumi.awsapigateway.RestAPIArgs;
import com.pulumi.awsapigateway.inputs.RequiredParameterArgs;
import com.pulumi.awsapigateway.inputs.RouteArgs;
import com.pulumi.awsapigateway.inputs.TargetArgs;
public class App {
public static void main(String[] args) {
Pulumi.run(ctx -> {
var api = new RestAPI("api", RestAPIArgs.builder()
.requestValidator(RequestValidator.PARAMS_ONLY)
.routes(new RouteArgs[]{
RouteArgs.builder()
.path("/search")
.method(Method.GET)
.target(TargetArgs.builder()
.type(IntegrationType.Http_proxy)
.uri("https://www.example.com/")
.build()
)
.requestValidator(RequestValidator.ALL)
.requiredParameters(List.of(RequiredParameterArgs.builder()
.name("q")
.in("query")
.build()))
.build(),
})
.build());
ctx.export("url", api.url());
});
}
}
name: awsx-apigateway-validation-types-yaml
runtime: yaml
description: An example of configuring request validators for an API Gateway instance.
resources:
api:
type: aws-apigateway:RestAPI
properties:
requestValidator: PARAMS_ONLY
routes:
- path: /search
method: GET
target:
type: http_proxy
uri: https://www.example.com/
requestValidator: ALL
requiredParameters:
- name: q
in: query
outputs:
url: ${api.url}
For more information about request validation, see Use Request Validation in API Gateway in the AWS documentation.
Use API Keys to Limit Requests
After you create, test, and deploy your APIs, you can use Amazon API Gateway usage plans to make them available to your customers. Usage plans and API keys allow customers to use your API at agreed-upon request rates and quotas that meet their business requirements and budget constraints. If desired, you can set API-level throttling limits.
To require an API key for a given route, set the route’s apiKeyRequired
property to true
, and at the API level,
specify whether the key should be provided in a HEADER
(specifically the x-api-key
header) or AUTHORIZER
(in which case a Lambda authorizer sends the API key as part of the authorization response). If unspecified, the default API key source is HEADER
.
Setting up API key-based authorization in API Gateway involves three steps:
- Creating the API key itself — for example, with an
ApiKey
resource - Defining a usage plan for the API (which can optionally define quotas and throttling rules)
- Associating the key and usage plan with the API Gateway instance
In the following example, an ApiKey
resource and UsagePlan
are provisioned and associated with a RestAPI
instance, and the generated API key value is exported as a Pulumi stack output (and tracked as an encrypted secret):
"use strict";
const aws = require("@pulumi/aws");
const apigateway = require("@pulumi/aws-apigateway");
const api = new apigateway.RestAPI("api", {
apiKeySource: "HEADER",
routes: [
{
path: "/",
localPath: "data",
contentType: "application/json",
index: "index.json",
apiKeyRequired: true,
},
],
});
const key = new aws.apigateway.ApiKey("key");
const plan = new aws.apigateway.UsagePlan("plan", {
apiStages: [
{
apiId: api.api.id,
stage: api.stage.stageName,
},
],
});
const planKey = new aws.apigateway.UsagePlanKey("plan-key", {
keyId: key.id,
keyType: "API_KEY",
usagePlanId: plan.id,
});
exports.url = api.url;
exports.apiKey = key.value;
import * as aws from "@pulumi/aws";
import * as apigateway from "@pulumi/aws-apigateway";
const api = new apigateway.RestAPI("api", {
apiKeySource: "HEADER",
routes: [
{
path: "/",
localPath: "data",
contentType: "application/json",
index: "index.json",
apiKeyRequired: true,
},
],
});
const key = new aws.apigateway.ApiKey("key");
const plan = new aws.apigateway.UsagePlan("plan", {
apiStages: [
{
apiId: api.api.id,
stage: api.stage.stageName,
},
],
});
const planKey = new aws.apigateway.UsagePlanKey("plan-key", {
keyId: key.id,
keyType: "API_KEY",
usagePlanId: plan.id,
});
export const url = api.url;
export const apiKey = key.value;
import pulumi
import pulumi_aws as aws
import pulumi_aws_apigateway as apigateway
api = apigateway.RestAPI(
"api",
apigateway.RestAPIArgs(
api_key_source=apigateway.APIKeySource.HEADER,
routes=[
apigateway.RouteArgs(
path="/",
method=apigateway.Method.GET,
local_path="data",
index="index.json",
content_type="application/json",
api_key_required=True,
),
],
)
)
key = aws.apigateway.ApiKey("key")
plan = aws.apigateway.UsagePlan("plan", aws.apigateway.UsagePlanArgs(
api_stages=[
aws.apigateway.UsagePlanApiStageArgs(
api_id=api.api.id,
stage=api.stage.stage_name,
),
],
))
plan_key = aws.apigateway.UsagePlanKey("plan-key", aws.apigateway.UsagePlanKeyArgs(
key_id=key.id,
key_type="API_KEY",
usage_plan_id=plan.id,
))
pulumi.export("url", api.url)
pulumi.export("apiKey", key.value)
package main
import (
"github.com/pulumi/pulumi-aws-apigateway/sdk/v2/go/apigateway"
awsapigateway "github.com/pulumi/pulumi-aws/sdk/v6/go/aws/apigateway"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
HEADER := apigateway.APIKeySourceHEADER
api, err := apigateway.NewRestAPI(ctx, "api", &apigateway.RestAPIArgs{
ApiKeySource: &HEADER,
Routes: []apigateway.RouteArgs{
{
Path: "/",
LocalPath: pulumi.StringRef("data"),
Index: pulumi.String("index.json"),
ContentType: pulumi.StringRef("application/json"),
ApiKeyRequired: pulumi.BoolRef(true),
},
},
})
if err != nil {
return err
}
key, err := awsapigateway.NewApiKey(ctx, "key", nil)
if err != nil {
return err
}
apiID := api.Api.ApplyT(func(api *awsapigateway.RestApi) pulumi.StringOutput {
return api.ID().ToStringOutput()
}).ApplyT(func(id interface{}) string {
return id.(string)
}).(pulumi.StringOutput)
plan, err := awsapigateway.NewUsagePlan(ctx, "plan", &awsapigateway.UsagePlanArgs{
ApiStages: awsapigateway.UsagePlanApiStageArray{
&awsapigateway.UsagePlanApiStageArgs{
ApiId: apiID,
Stage: api.Stage.StageName(),
},
},
})
if err != nil {
return err
}
_, err = awsapigateway.NewUsagePlanKey(ctx, "plan-key", &awsapigateway.UsagePlanKeyArgs{
KeyId: key.ID(),
KeyType: pulumi.String("API_KEY"),
UsagePlanId: plan.ID(),
})
if err != nil {
return err
}
ctx.Export("url", api.Url)
ctx.Export("apiKey", key.Value)
return nil
})
}
using System.Collections.Generic;
using Pulumi;
using Aws = Pulumi.Aws;
using AwsApiGateway = Pulumi.AwsApiGateway;
using Pulumi.AwsApiGateway.Inputs;
return await Deployment.RunAsync(() =>
{
var api = new AwsApiGateway.RestAPI("api", new()
{
ApiKeySource = AwsApiGateway.APIKeySource.HEADER,
Routes =
{
new RouteArgs()
{
Path = "/",
LocalPath = "data",
Index = "index.json",
ContentType = "application/json",
ApiKeyRequired = true,
},
},
});
var key = new Aws.ApiGateway.ApiKey("key");
var plan = new Aws.ApiGateway.UsagePlan("plan", new()
{
ApiStages = {
new Aws.ApiGateway.Inputs.UsagePlanApiStageArgs()
{
ApiId = api.Api.Apply(api => api.Id),
Stage = api.Stage.Apply(stage => stage.StageName),
},
},
});
var planKey = new Aws.ApiGateway.UsagePlanKey("plan-key", new()
{
KeyId = key.Id,
KeyType = "API_KEY",
UsagePlanId = plan.Id,
});
return new Dictionary<string, object?>
{
["url"] = api.Url,
["apiKey"] = key.Value,
};
});
package myproject;
import java.util.List;
import com.pulumi.Pulumi;
import com.pulumi.aws.apigateway.ApiKey;
import com.pulumi.aws.apigateway.UsagePlan;
import com.pulumi.aws.apigateway.UsagePlanArgs;
import com.pulumi.aws.apigateway.UsagePlanKey;
import com.pulumi.aws.apigateway.UsagePlanKeyArgs;
import com.pulumi.aws.apigateway.inputs.UsagePlanApiStageArgs;
import com.pulumi.awsapigateway.enums.APIKeySource;
import com.pulumi.awsapigateway.enums.Method;
import com.pulumi.awsapigateway.RestAPI;
import com.pulumi.awsapigateway.RestAPIArgs;
import com.pulumi.awsapigateway.inputs.RouteArgs;
public class App {
public static void main(String[] args) {
Pulumi.run(ctx -> {
var api = new RestAPI("api", RestAPIArgs.builder()
.apiKeySource(APIKeySource.HEADER)
.routes(new RouteArgs[]{
RouteArgs.builder()
.path("/")
.method(Method.GET)
.localPath("data")
.index(("index.json"))
.contentType("application/json")
.apiKeyRequired(true)
.build(),
})
.build());
var key = new ApiKey("key");
var plan = new UsagePlan("plan", UsagePlanArgs.builder()
.apiStages(List.of(UsagePlanApiStageArgs.builder()
.apiId(api.api().apply(a -> a.id()))
.stage(api.stage().apply(s -> s.stageName()))
.build()))
.build());
var planKey = new UsagePlanKey("plan-key", UsagePlanKeyArgs.builder()
.keyId(key.id())
.keyType("API_KEY")
.usagePlanId(plan.id())
.build());
ctx.export("url", api.url());
ctx.export("apiKey", key.value());
});
}
}
name: awsx-apigateway-api-keys-yaml
runtime: yaml
description: An example that deploys an API Gateway instance with support for API keys.
resources:
api:
type: aws-apigateway:RestAPI
properties:
routes:
- path: /
localPath: data
index: index.json
contentType: application/json
apiKeyRequired: true
key:
type: aws:apigateway:ApiKey
plan:
type: aws:apigateway:UsagePlan
properties:
apiStages:
- apiId: ${api.api.id}
stage: ${api.stage.stageName}
planKey:
type: aws:apigateway:UsagePlanKey
properties:
keyId: ${key.id}
keyType: "API_KEY"
usagePlanId: ${plan.id}
outputs:
url: ${api.url}
apiKey: ${key.value}
Note that when using the HEADER
API key source, requests are expected to provide the token in a header named x-api-key
:
$ curl -H "x-api-key: $(pulumi stack output apiKey --show-secrets)" $(pulumi stack output url)
{
"message": "Hello from API Gateway!"
}
$ curl $(pulumi stack output url)
{"message":"Unauthorized"}
For more information about Usage Plans and API Keys, refer to Create and Use Usage Plans with API Keys.
Custom Domains and SSL
API Gateway automatically provisions and assigns a domain name, URL that contains the stage, and SSL
support. Generated URLs look something like https://no90ji5v23.execute-api.us-west-2.amazonaws.com/stage/
, where the host
portion of the URL refers to an API endpoint that can be edge-optimized or regional.
Although it’s great to have a URL with SSL support that works immediately, the resulting URL isn’t user-friendly or very easy to remember, and may not be suitable for business scenarios that require using company domains.
To provide a simpler and more intuitive URL for your API users, you can configure a custom domain name for your API Gateway instance (such as api.example.com
) and customize the base path of the URL to be something other than /stage
. When you configure a custom domain, API Gateway also sets up an edge-optimized Amazon CloudFront Content Distribution Network (CDN) for you.
The following example shows how to configure an API Gateway instance with a custom domain using Route53 and a free SSL certificate from AWS Certificate Manager (ACM). (ACM also supports importing certificates issued by third-party certificate authorities.) Note that the ACM certificate is created in the us-east-1
region (a CloudFront requirement) and validated with a CertificateValidation
resource that verifies domain ownership through Route53 DNS.
const aws = require("@pulumi/aws");
const pulumi = require("@pulumi/pulumi");
const apigateway = require("@pulumi/aws-apigateway");
const hostedZoneName = "example.com";
const domainName = `myapp.${hostedZoneName}`;
// Look up your existing Route 53 hosted zone.
const zone = aws.route53.getZoneOutput({ name: hostedZoneName });
const usEast1 = new aws.Provider("us-east-1", {
region: "us-east-1",
});
// Provision a new ACM certificate.
const certificate = new aws.acm.Certificate(
"certificate",
{
domainName: domainName,
validationMethod: "DNS",
},
{ provider: usEast1 },
);
// Validate the ACM certificate with DNS.
const validationOption = certificate.domainValidationOptions[0];
const validationRecord = new aws.route53.Record("certificate-validation-record", {
name: validationOption.resourceRecordName,
type: validationOption.resourceRecordType,
records: [validationOption.resourceRecordValue],
zoneId: zone.zoneId,
ttl: 60,
});
const validation = new aws.acm.CertificateValidation(
"certificate-validation",
{
certificateArn: certificate.arn,
validationRecordFqdns: [validationRecord.fqdn],
},
{ provider: usEast1 },
);
const api = new apigateway.RestAPI("api", {
routes: [
{
path: "/",
localPath: "www",
},
],
});
const gatewayDomainName = new aws.apigateway.DomainName(
"gateway-domain-name",
{
certificateArn: certificate.arn,
domainName,
},
{ dependsOn: validation },
);
const gatewayDNSRecord = new aws.route53.Record("gateway-dns-record", {
zoneId: zone.zoneId,
type: "A",
name: domainName,
aliases: [
{
name: gatewayDomainName.cloudfrontDomainName,
zoneId: gatewayDomainName.cloudfrontZoneId,
evaluateTargetHealth: false,
},
],
});
const basePathMapping = new aws.apigateway.BasePathMapping("gateway-path-mapping", {
restApi: api.api.id,
stageName: api.stage.stageName,
domainName: gatewayDomainName.domainName,
});
exports.url = pulumi.interpolate`https://${basePathMapping.domainName}`;
import * as aws from "@pulumi/aws";
import * as pulumi from "@pulumi/pulumi";
import * as apigateway from "@pulumi/aws-apigateway";
const hostedZoneName = "example.com";
const domainName = `myapp.${hostedZoneName}`;
const zone = aws.route53.getZoneOutput({ name: hostedZoneName });
const usEast1 = new aws.Provider("us-east-1", {
region: "us-east-1",
});
const certificate = new aws.acm.Certificate(
"certificate",
{
domainName: domainName,
validationMethod: "DNS",
},
{ provider: usEast1 },
);
const validationOption = certificate.domainValidationOptions[0];
const validationRecord = new aws.route53.Record("certificate-validation-record", {
name: validationOption.resourceRecordName,
type: validationOption.resourceRecordType,
records: [validationOption.resourceRecordValue],
zoneId: zone.zoneId,
ttl: 60,
});
const validation = new aws.acm.CertificateValidation(
"certificate-validation",
{
certificateArn: certificate.arn,
validationRecordFqdns: [validationRecord.fqdn],
},
{ provider: usEast1 },
);
const api = new apigateway.RestAPI("api", {
routes: [
{
path: "/",
localPath: "www",
},
],
});
const gatewayDomainName = new aws.apigateway.DomainName(
"gateway-domain-name",
{
certificateArn: certificate.arn,
domainName,
},
{ dependsOn: validation },
);
const gatewayDNSRecord = new aws.route53.Record("gateway-dns-record", {
zoneId: zone.zoneId,
type: "A",
name: domainName,
aliases: [
{
name: gatewayDomainName.cloudfrontDomainName,
zoneId: gatewayDomainName.cloudfrontZoneId,
evaluateTargetHealth: false,
},
],
});
const basePathMapping = new aws.apigateway.BasePathMapping("gateway-path-mapping", {
restApi: api.api.id,
stageName: api.stage.stageName,
domainName: gatewayDomainName.domainName,
});
export const url = pulumi.interpolate`https://${basePathMapping.domainName}`;
import pulumi
import pulumi_aws as aws
import pulumi_aws_apigateway as apigateway
hosted_zone_name = "example.com"
domain_name = f"myapp.{hosted_zone_name}"
# Look up your existing Route 53 hosted zone.
zone = aws.route53.get_zone(name=hosted_zone_name)
us_east_1 = aws.Provider(
"us-east-1",
aws.ProviderArgs(
region="us-east-1",
),
)
# Provision a new ACM certificate.
certificate = aws.acm.Certificate(
"certificate",
aws.acm.CertificateArgs(
domain_name=domain_name,
validation_method="DNS",
),
opts=pulumi.ResourceOptions(provider=us_east_1),
)
validation_option = certificate.domain_validation_options[0]
validation_record = aws.route53.Record(
"certificate-validation-record",
aws.route53.RecordArgs(
name=validation_option["resource_record_name"],
type=validation_option["resource_record_type"],
records=[validation_option["resource_record_value"]],
zone_id=zone.zone_id,
ttl=60,
),
)
validation = aws.acm.CertificateValidation(
"certificate-validation",
aws.acm.CertificateValidationArgs(
certificate_arn=certificate.arn,
validation_record_fqdns=[validation_record.fqdn],
),
opts=pulumi.ResourceOptions(provider=us_east_1),
)
api = apigateway.RestAPI(
"api",
routes=[
apigateway.RouteArgs(
path="/",
method=apigateway.Method.GET,
local_path="www",
)
],
)
gatewayDomainName = aws.apigateway.DomainName(
"gateway-domain-name",
aws.apigateway.DomainNameArgs(
certificate_arn=certificate.arn,
domain_name=domain_name,
),
opts=pulumi.ResourceOptions(depends_on=validation),
)
gateway_dns_record = aws.route53.Record(
"gateway-dns-record",
aws.route53.RecordArgs(
zone_id=zone.zone_id,
type="A",
name=domain_name,
aliases=[
aws.route53.RecordAliasArgs(
name=gatewayDomainName.cloudfront_domain_name,
zone_id=gatewayDomainName.cloudfront_zone_id,
evaluate_target_health=False,
)
],
),
)
base_path_mapping = aws.apigateway.BasePathMapping(
"gateway-path-mapping",
aws.apigateway.BasePathMappingArgs(
rest_api=api.api.id,
stage_name=api.stage.stage_name,
domain_name=gatewayDomainName.domain_name,
),
)
pulumi.export("url", pulumi.Output.concat("https://", base_path_mapping.domain_name))
package main
import (
"fmt"
"github.com/pulumi/pulumi-aws-apigateway/sdk/v2/go/apigateway"
"github.com/pulumi/pulumi-aws/sdk/v6/go/aws"
"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/acm"
awsapigateway "github.com/pulumi/pulumi-aws/sdk/v6/go/aws/apigateway"
"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/route53"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
hostedZone := "example.com"
domainName := fmt.Sprintf("myapp.%s", hostedZone)
zone := route53.LookupZoneOutput(ctx, route53.LookupZoneOutputArgs{
Name: pulumi.StringPtr(hostedZone),
})
usEast1, err := aws.NewProvider(ctx, "us-east-1", &aws.ProviderArgs{
Region: pulumi.StringPtr("us-east-1"),
})
if err != nil {
return err
}
certificate, err := acm.NewCertificate(ctx, "certificate", &acm.CertificateArgs{
DomainName: pulumi.String(domainName),
ValidationMethod: pulumi.String("DNS"),
}, pulumi.Provider(usEast1))
if err != nil {
return err
}
validationOption := certificate.DomainValidationOptions.Index(pulumi.Int(0))
validationRecord, err := route53.NewRecord(ctx, "certificate-validation-record", &route53.RecordArgs{
Name: validationOption.ResourceRecordName().Elem(),
Type: validationOption.ResourceRecordType().Elem(),
Records: pulumi.StringArray{
validationOption.ResourceRecordValue().Elem(),
},
ZoneId: zone.ZoneId(),
Ttl: pulumi.Int(60),
})
if err != nil {
return err
}
validation, err := acm.NewCertificateValidation(ctx, "certificate-validation", &acm.CertificateValidationArgs{
CertificateArn: certificate.Arn,
ValidationRecordFqdns: pulumi.StringArray{
validationRecord.Fqdn,
},
}, pulumi.Provider(usEast1))
if err != nil {
return err
}
methodGET := apigateway.MethodGET
api, err := apigateway.NewRestAPI(ctx, "api", &apigateway.RestAPIArgs{
Routes: []apigateway.RouteArgs{
{
Path: "/",
Method: &methodGET,
LocalPath: pulumi.StringRef("www"),
},
},
})
if err != nil {
return err
}
gatewayDomainName, err := awsapigateway.NewDomainName(ctx, "gateway-domain-name", &awsapigateway.DomainNameArgs{
CertificateArn: certificate.Arn,
DomainName: pulumi.String(domainName),
}, pulumi.DependsOn([]pulumi.Resource{validation}))
if err != nil {
return err
}
_, err = route53.NewRecord(ctx, "gateway-dns-record", &route53.RecordArgs{
ZoneId: zone.ZoneId(),
Type: pulumi.String("A"),
Name: pulumi.String(domainName),
Aliases: route53.RecordAliasArray{
route53.RecordAliasArgs{
Name: gatewayDomainName.CloudfrontDomainName,
ZoneId: gatewayDomainName.CloudfrontZoneId,
EvaluateTargetHealth: pulumi.Bool(false),
},
},
})
if err != nil {
return err
}
apiID := api.Api.ApplyT(func(api *awsapigateway.RestApi) pulumi.StringOutput {
return api.ID().ToStringOutput()
})
basePathMapping, err := awsapigateway.NewBasePathMapping(ctx, "gateway-path-mapping", &awsapigateway.BasePathMappingArgs{
RestApi: apiID,
StageName: api.Stage.StageName(),
DomainName: gatewayDomainName.DomainName,
})
if err != nil {
return err
}
ctx.Export("url", pulumi.Sprintf("http://%s", basePathMapping.DomainName))
return nil
})
}
using System.Collections.Generic;
using Pulumi;
using ApiGateway = Pulumi.AwsApiGateway;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var hostedZoneName = "example.com";
var domainName = $"myapp.{hostedZoneName}";
var zone = Aws.Route53.GetZone.Invoke(new()
{
Name = hostedZoneName,
});
var usEast1 = new Aws.Provider("us-east-1", new()
{
Region = "us-east-1",
});
var certificate = new Aws.Acm.Certificate("certificate", new() {
DomainName = domainName,
ValidationMethod = "DNS",
}, new CustomResourceOptions { Provider = usEast1 });
var validationOption = certificate.DomainValidationOptions.GetAt(0);
var validationRecord = new Aws.Route53.Record("certificate-validation", new()
{
Name = validationOption.Apply(option => option.ResourceRecordName!),
Type = validationOption.Apply(option => option.ResourceRecordType!),
Records = { validationOption.Apply(option => option.ResourceRecordValue!), },
ZoneId = zone.Apply(zone => zone.ZoneId),
Ttl = 60,
});
var validation = new Aws.Acm.CertificateValidation("certificate-validation", new()
{
CertificateArn = certificate.Arn,
ValidationRecordFqdns = { validationRecord.Fqdn },
}, new CustomResourceOptions { Provider = usEast1 });
var api = new ApiGateway.RestAPI("api", new()
{
Routes =
{
new ApiGateway.Inputs.RouteArgs {
Path = "/",
LocalPath = "www",
},
},
});
var gatewayDomainName = new Aws.ApiGateway.DomainName("gateway-domain-name", new()
{
CertificateArn = certificate.Arn,
Domain = domainName,
}, new CustomResourceOptions { DependsOn = validation });
var gatewayDNSRecord = new Aws.Route53.Record("gateway-dns-record", new()
{
ZoneId = zone.Apply(zone => zone.ZoneId),
Type = "A",
Name = domainName,
Aliases =
{
new Aws.Route53.Inputs.RecordAliasArgs
{
Name = gatewayDomainName.CloudfrontDomainName,
ZoneId = gatewayDomainName.CloudfrontZoneId,
EvaluateTargetHealth = true,
},
},
});
var basePathMapping = new Aws.ApiGateway.BasePathMapping("gateway-path-mapping", new()
{
RestApi = api.Api.Apply(api => api.Id),
StageName = api.Stage.Apply(stage => stage.StageName),
DomainName = gatewayDomainName.Domain,
});
return new Dictionary<string, object?>
{
["url"] = Output.Format($"https://{basePathMapping.DomainName}/"),
};
});
package myproject;
import java.util.List;
import com.pulumi.Pulumi;
import com.pulumi.core.Either;
import com.pulumi.core.Output;
import com.pulumi.resources.CustomResourceOptions;
import com.pulumi.aws.Provider;
import com.pulumi.aws.ProviderArgs;
import com.pulumi.aws.acm.Certificate;
import com.pulumi.aws.acm.CertificateArgs;
import com.pulumi.aws.acm.CertificateValidation;
import com.pulumi.aws.acm.CertificateValidationArgs;
import com.pulumi.aws.apigateway.BasePathMapping;
import com.pulumi.aws.apigateway.BasePathMappingArgs;
import com.pulumi.aws.apigateway.DomainName;
import com.pulumi.aws.apigateway.DomainNameArgs;
import com.pulumi.aws.route53.Record;
import com.pulumi.aws.route53.RecordArgs;
import com.pulumi.aws.route53.Route53Functions;
import com.pulumi.aws.route53.inputs.GetZoneArgs;
import com.pulumi.aws.route53.inputs.RecordAliasArgs;
import com.pulumi.awsapigateway.RestAPI;
import com.pulumi.awsapigateway.RestAPIArgs;
import com.pulumi.awsapigateway.inputs.RouteArgs;
public class App {
public static void main(String[] args) {
Pulumi.run(ctx -> {
var hostedZoneName = "example.com";
var domainName = String.format("myapp.%s", hostedZoneName);
var zone = Route53Functions.getZone(GetZoneArgs.builder()
.name(hostedZoneName)
.build());
var usEast1 = new Provider("us-east-1", ProviderArgs.builder()
.region("us-east-1")
.build());
var certificate = new Certificate("cert", CertificateArgs.builder()
.domainName(domainName)
.validationMethod("DNS")
.build(), CustomResourceOptions.builder()
.provider(usEast1)
.build());
var validationOption = certificate.domainValidationOptions().applyValue(o -> o.get(0));
var validationRecord = new Record("certificate-validation-record", RecordArgs.builder()
.name(validationOption.apply(o -> Output.of(o.resourceRecordName().get())))
.type(validationOption.apply(o -> Output.of(Either.ofLeft(o.resourceRecordType().get()))))
.zoneId(zone.applyValue(z -> z.zoneId()))
.records(validationOption.applyValue(o -> List.of(o.resourceRecordValue().get())))
.ttl(60)
.build());
var validation = new CertificateValidation("certificate-validation", CertificateValidationArgs.builder()
.certificateArn(certificate.arn())
.validationRecordFqdns(validationRecord.fqdn().applyValue(fqdn -> List.of(fqdn)))
.build(), CustomResourceOptions.builder()
.provider(usEast1)
.build());
var api = new RestAPI("api", RestAPIArgs.builder()
.routes(new RouteArgs[]{
RouteArgs.builder()
.path("/")
.localPath("www")
.build()
})
.build());
var gatewayDomainName = new DomainName("gateway-domain-name", DomainNameArgs.builder()
.certificateArn(certificate.arn())
.domainName(domainName)
.build(), CustomResourceOptions.builder()
.dependsOn(List.of(validation))
.build());
var gatewayDNSRecord = new Record("gateway-dns-record", RecordArgs.builder()
.zoneId(zone.applyValue(z -> z.zoneId()))
.type("A")
.name(domainName)
.aliases(RecordAliasArgs.builder()
.name(gatewayDomainName.cloudfrontDomainName())
.zoneId(gatewayDomainName.cloudfrontZoneId())
.evaluateTargetHealth(false)
.build())
.build());
var basePathMapping = new BasePathMapping("gateway-path-mapping", BasePathMappingArgs.builder()
.restApi(api.api().apply(v -> v.id()))
.stageName(api.stage().apply(n -> n.stageName()))
.domainName(gatewayDomainName.domainName())
.build());
ctx.export("url", Output.format("https://%s", basePathMapping.domainName()));
});
}
}
name: awsx-apigateway-custom-domain-yaml
runtime: yaml
description: An example that deploys an API Gateway REST API with a custom domain from Route53.
variables:
hostedZoneName: example.com
domainName: myapp.${hostedZoneName}
zone:
fn::invoke:
function: aws:route53:getZone
arguments:
name: ${hostedZoneName}
resources:
usEast1:
type: pulumi:providers:aws
properties:
region: us-east-1
certificate:
type: aws:acm:Certificate
properties:
domainName: ${domainName}
validationMethod: DNS
options:
provider: ${usEast1}
validationRecord:
type: aws:route53:Record
properties:
name: ${certificate.domainValidationOptions[0].resourceRecordName}
type: ${certificate.domainValidationOptions[0].resourceRecordType}
records:
- ${certificate.domainValidationOptions[0].resourceRecordValue}
zoneId: ${zone.zoneId}
ttl: 60
validation:
type: aws:acm:CertificateValidation
properties:
certificateArn: ${certificate.arn}
validationRecordFqdns:
- ${validationRecord.fqdn}
options:
provider: ${usEast1}
api:
type: aws-apigateway:RestAPI
properties:
routes:
- path: /
localPath: www
gatewayDomainName:
type: aws:apigateway:DomainName
properties:
certificateArn: ${certificate.arn}
domainName: ${domainName}
options:
dependsOn:
- ${validation}
gatewayDNSRecord:
type: aws:route53:Record
properties:
zoneId: ${zone.zoneId}
type: A
name: ${domainName}
aliases:
- name: ${gatewayDomainName.cloudfrontDomainName}
zoneId: ${gatewayDomainName.cloudfrontZoneId}
evaluateTargetHealth: false
basePathMapping:
type: aws:apigateway:BasePathMapping
properties:
restApi: ${api.api.id}
stageName: ${api.stage.stageName}
domainName: ${gatewayDomainName.domainName}
outputs:
url: https://${basePathMapping.domainName}
For more information about the options and levels of customizability available for edge-optimized API Gateways and custom domains, refer to Set up Custom Domain Name for an API in API Gateway in the AWS documentation.
OpenAPI
Amazon API Gateway supports the OpenAPI specification (formerly known as “Swagger”) for defining APIs. Using OpenAPI to define your APIs eases integration with other API authoring, modeling, and testing tools, at some added complexity cost as you will need to understand the mechanics of how API Gateway works and what HTTP headers it uses to accomplish its integrations.
Defining an Entire Endpoint
To use an OpenAPI specification to initialize your API Gateway, supply an entire OpenAPI specification as a JSON string
in the swaggerString
property. For example, this API proxies a route through to another HTTP endpoint by setting up an http_proxy
integration, much like the example above:
"use strict";
const pulumi = require("@pulumi/pulumi");
const apigateway = require("@pulumi/aws-apigateway");
const api = new apigateway.RestAPI("api", {
swaggerString: JSON.stringify({
"swagger": "2.0",
"info": {
title: "example",
version: "1.0",
},
"paths": {
"/": {
get: {
"x-amazon-apigateway-integration": {
httpMethod: "GET",
passthroughBehavior: "when_no_match",
type: "http_proxy",
uri: "https://httpbin.org/uuid",
},
},
},
},
"x-amazon-apigateway-binary-media-types": ["*/*"],
}),
});
exports.url = api.url;
import * as pulumi from "@pulumi/pulumi";
import * as apigateway from "@pulumi/aws-apigateway";
const api = new apigateway.RestAPI("api", {
swaggerString: JSON.stringify({
"swagger": "2.0",
"info": {
title: "example",
version: "1.0",
},
"paths": {
"/": {
get: {
"x-amazon-apigateway-integration": {
httpMethod: "GET",
passthroughBehavior: "when_no_match",
type: "http_proxy",
uri: "https://httpbin.org/uuid",
},
},
},
},
"x-amazon-apigateway-binary-media-types": ["*/*"],
}),
});
export const url = api.url;
import json
import pulumi
import pulumi_aws_apigateway as apigateway
api = apigateway.RestAPI("api", apigateway.RestAPIArgs(
swagger_string=json.dumps(
{
"swagger": "2.0",
"info": {
"title": "example",
"version": "1.0",
},
"paths": {
"/": {
"get": {
"x-amazon-apigateway-integration": {
"httpMethod": "GET",
"passthroughBehavior": "when_no_match",
"type": "http_proxy",
"uri": "https://httpbin.org/uuid",
},
},
},
},
"x-amazon-apigateway-binary-media-types": ["*/*"],
}
)),
)
pulumi.export("url", api.url)
package main
import (
"github.com/pulumi/pulumi-aws-apigateway/sdk/v2/go/apigateway"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
api, err := apigateway.NewRestAPI(ctx, "api", &apigateway.RestAPIArgs{
SwaggerString: pulumi.String(`{
"swagger": "2.0",
"info": {
"title": "example",
"version": "1.0"
},
"paths": {
"/": {
"get": {
"x-amazon-apigateway-integration": {
"httpMethod": "GET",
"passthroughBehavior": "when_no_match",
"type": "http_proxy",
"uri": "https://httpbin.org/uuid"
}
}
}
},
"x-amazon-apigateway-binary-media-types": ["*/*"]
}`),
})
if err != nil {
return err
}
ctx.Export("url", api.Url)
return nil
})
}
using System;
using System.Collections.Generic;
using System.Text.Json;
using Pulumi;
using AwsApiGateway = Pulumi.AwsApiGateway;
return await Deployment.RunAsync(() =>
{
var api = new AwsApiGateway.RestAPI("api", new()
{
SwaggerString = JsonSerializer.Serialize(new Dictionary<string, object>() {
["swagger"] = "2.0",
["info"] = new {
title = "example",
version = "1.0",
},
["paths"] = new Dictionary<string, object>() {
["/"] = new {
get = new Dictionary<string, object>() {
["x-amazon-apigateway-integration"] = new {
httpMethod = "GET",
passthroughBehavior = "when_no_match",
type = "http_proxy",
uri = "https://httpbin.org/uuid",
},
},
},
},
["x-amazon-apigateway-binary-media-types"] = new[] {
"*/*"
},
}),
});
return new Dictionary<string, object?>
{
["url"] = api.Url,
};
});
package myproject;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.s3.Bucket;
import com.pulumi.awsapigateway.RestAPI;
import com.pulumi.awsapigateway.RestAPIArgs;
import static com.pulumi.codegen.internal.Serialization.*;
public class App {
public static void main(String[] args) {
Pulumi.run(ctx -> {
var api = new RestAPI("api", RestAPIArgs.builder()
.swaggerString(serializeJson(jsonObject(
jsonProperty("swagger", "2.0"),
jsonProperty("info", jsonObject(
jsonProperty("title", "example"),
jsonProperty("version", "1.0")
)),
jsonProperty("paths", jsonObject(
jsonProperty("/", jsonObject(
jsonProperty("get", jsonObject(
jsonProperty("x-amazon-apigateway-integration", jsonObject(
jsonProperty("httpMethod", "GET"),
jsonProperty("passthroughBehavior", "when_no_match"),
jsonProperty("type", "http_proxy"),
jsonProperty("uri", "https://httpbin.org/uuid")
))
))
))
)),
jsonProperty("x-amazon-apigateway-binary-media-types", jsonArray(
"*/*"
))
)))
.build());
ctx.export("url", api.url());
});
}
}
name: awsx-apigateway-openapi-full-yaml
runtime: yaml
description: An example of configuring an API Gateway instance with an OpenAPI/Swagger JSON definition.
resources:
api:
type: aws-apigateway:RestAPI
properties:
swaggerString: |
{
"swagger": "2.0",
"info": {
"title": "example",
"version": "1.0"
},
"paths": {
"/": {
"get": {
"x-amazon-apigateway-integration": {
"httpMethod": "GET",
"passthroughBehavior": "when_no_match",
"type": "http_proxy",
"uri": "https://httpbin.org/uuid"
}
}
}
},
"x-amazon-apigateway-binary-media-types": ["*/*"]
}
outputs:
url: ${api.url}
Requests for the exported URL are passed directly to the configured backend:
$ curl $(pulumi stack output url)
{
"uuid": "afb17cf3-7a70-4c4b-9f09-7e8f23a63b2b"
}
This is more complex than the above examples, but an escape hatch that you can use to access any API Gateway features not yet supported by the easier abstractions in Pulumi Crosswalk for AWS API Gateway. You must manually provide permission for any route targets to be invoked by API Gateway when using this option.
Defining a Single Route
Being able to provide an OpenAPI specification for an entire API Gateway lets you take matters into your own
hands when you need access to a feature that isn’t supported directly by apigateway.RestAPI
. However, you can also
define just a single API route (a “raw data” route) using OpenAPI using the data
property.
The data
property is just the x-amazon-apigateway-integration
object, which can be seen in the above example.
The route’s other parameters, such as its path and method, otherwise use the same approaches seen earlier.
For instance, the same API Gateway endpoint that proxies through to another API can be authored as follows:
"use strict";
const apigateway = require("@pulumi/aws-apigateway");
const api = new apigateway.RestAPI("api", {
routes: [
{
path: "/",
method: "GET",
data: {
"x-amazon-apigateway-integration": {
httpMethod: "GET",
passthroughBehavior: "when_no_match",
type: "http_proxy",
uri: "https://httpbin.org/uuid",
},
},
},
],
});
exports.url = api.url;
const apigateway = require("@pulumi/aws-apigateway");
const api = new apigateway.RestAPI("api", {
routes: [
{
path: "/",
method: "GET",
data: {
"x-amazon-apigateway-integration": {
httpMethod: "GET",
passthroughBehavior: "when_no_match",
type: "http_proxy",
uri: "https://httpbin.org/uuid",
},
},
},
],
});
exports.url = api.url;
import pulumi
import pulumi_aws_apigateway as apigateway
api = apigateway.RestAPI(
"api",
routes=[
apigateway.RouteArgs(
path="/",
method=apigateway.Method.GET,
data={
"x-amazon-apigateway-integration": {
"httpMethod": "GET",
"passthroughBehavior": "when_no_match",
"type": "http_proxy",
"uri": "https://httpbin.org/uuid",
},
},
),
],
)
pulumi.export("url", api.url)
package main
import (
"github.com/pulumi/pulumi-aws-apigateway/sdk/v2/go/apigateway"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
GET := apigateway.MethodGET
api, err := apigateway.NewRestAPI(ctx, "api", &apigateway.RestAPIArgs{
Routes: []apigateway.RouteArgs{
{
Path: "/",
Method: &GET,
Data: map[string]interface{}{
"x-amazon-apigateway-integration": map[string]interface{}{
"httpMethod": "GET",
"passthroughBehavior": "when_no_match",
"type": "http_proxy",
"uri": "https://httpbin.org/uuid",
},
},
},
},
})
if err != nil {
return err
}
ctx.Export("url", api.Url)
return nil
})
}
using System.Collections.Generic;
using System.Text.Json;
using Pulumi;
using AwsApiGateway = Pulumi.AwsApiGateway;
return await Deployment.RunAsync(() =>
{
var api = new AwsApiGateway.RestAPI("api", new()
{
Routes =
{
new AwsApiGateway.Inputs.RouteArgs
{
Path = "/",
Method = AwsApiGateway.Method.GET,
Data = JsonSerializer.Deserialize<object>(JsonSerializer.Serialize(new Dictionary<string, object>() {
["x-amazon-apigateway-integration"] = new {
httpMethod = "GET",
passthroughBehavior = "when_no_match",
type = "http_proxy",
uri = "https://httpbin.org/uuid",
},
})),
},
},
});
return new Dictionary<string, object?>
{
["url"] = api.Url,
};
});
package myproject;
import com.pulumi.Pulumi;
import com.pulumi.awsapigateway.enums.Method;
import com.pulumi.awsapigateway.RestAPI;
import com.pulumi.awsapigateway.RestAPIArgs;
import com.pulumi.awsapigateway.inputs.RouteArgs;
import static com.pulumi.codegen.internal.Serialization.*;
public class App {
public static void main(String[] args) {
Pulumi.run(ctx -> {
var api = new RestAPI("api", RestAPIArgs.builder()
.routes(new RouteArgs[]{
RouteArgs.builder()
.path("/")
.method(Method.GET)
.data(jsonObject(
jsonProperty("x-amazon-apigateway-integration", jsonObject(
jsonProperty("httpMethod", "GET"),
jsonProperty("passthroughBehavior", "when_no_match"),
jsonProperty("type", "http_proxy"),
jsonProperty("uri", "https://httpbin.org/uuid")
))
))
.build(),
})
.build());
ctx.export("url", api.url());
});
}
}
name: awsx-apigateway-openapi-route-yaml
runtime: yaml
description: n example of configuring an API Gateway route with an OpenAPI/Swagger definition.
outputs:
# Export the name of the bucket
bucketName: ${my-bucket.id}
resources:
# Create an AWS resource (S3 Bucket)
my-bucket:
type: aws:s3:Bucket
Requests are handled similarly:
$ curl $(pulumi stack output url)
{
"uuid": "6cfb94ae-1655-4697-b413-89e541b027f4"
}
For more information about API Gateway’s support for OpenAPI, including exporting specifications from existing APIs for consumption from other tools, see Documenting a REST API in API Gateway in the AWS documentation. For full details on what the OpenAPI integration object may contain, see the x-amazon-apigateway-integration Object documentation.
Additional API Gateway Resources
For more details about Amazon API Gateway and REST APIs, see the following resources:
Thank you for your feedback!
If you have a question about how to use Pulumi, reach out in Community Slack.
Open an issue on GitHub to report a problem or suggest an improvement.