1 | # Amazon API Gateway Construct Library
|
2 |
|
3 |
|
4 | ---
|
5 |
|
6 | ![cfn-resources: Stable](https://img.shields.io/badge/cfn--resources-stable-success.svg?style=for-the-badge)
|
7 |
|
8 | ![cdk-constructs: Stable](https://img.shields.io/badge/cdk--constructs-stable-success.svg?style=for-the-badge)
|
9 |
|
10 | ---
|
11 |
|
12 |
|
13 |
|
14 | Amazon API Gateway is a fully managed service that makes it easy for developers
|
15 | to publish, maintain, monitor, and secure APIs at any scale. Create an API to
|
16 | access data, business logic, or functionality from your back-end services, such
|
17 | as applications running on Amazon Elastic Compute Cloud (Amazon EC2), code
|
18 | running on AWS Lambda, or any web application.
|
19 |
|
20 | ## Table of Contents
|
21 |
|
22 | - [Defining APIs](#defining-apis)
|
23 | - [Breaking up Methods and Resources across Stacks](#breaking-up-methods-and-resources-across-stacks)
|
24 | - [AWS Lambda-backed APIs](#aws-lambda-backed-apis)
|
25 | - [AWS StepFunctions backed APIs](#aws-stepfunctions-backed-APIs)
|
26 | - [Integration Targets](#integration-targets)
|
27 | - [Usage Plan & API Keys](#usage-plan--api-keys)
|
28 | - [Working with models](#working-with-models)
|
29 | - [Default Integration and Method Options](#default-integration-and-method-options)
|
30 | - [Proxy Routes](#proxy-routes)
|
31 | - [Authorizers](#authorizers)
|
32 | - [IAM-based authorizer](#iam-based-authorizer)
|
33 | - [Lambda-based token authorizer](#lambda-based-token-authorizer)
|
34 | - [Lambda-based request authorizer](#lambda-based-request-authorizer)
|
35 | - [Cognito User Pools authorizer](#cognito-user-pools-authorizer)
|
36 | - [Mutual TLS](#mutal-tls-mtls)
|
37 | - [Deployments](#deployments)
|
38 | - [Deep dive: Invalidation of deployments](#deep-dive-invalidation-of-deployments)
|
39 | - [Custom Domains](#custom-domains)
|
40 | - [Access Logging](#access-logging)
|
41 | - [Cross Origin Resource Sharing (CORS)](#cross-origin-resource-sharing-cors)
|
42 | - [Endpoint Configuration](#endpoint-configuration)
|
43 | - [Private Integrations](#private-integrations)
|
44 | - [Gateway Response](#gateway-response)
|
45 | - [OpenAPI Definition](#openapi-definition)
|
46 | - [Endpoint configuration](#endpoint-configuration)
|
47 | - [Metrics](#metrics)
|
48 | - [APIGateway v2](#apigateway-v2)
|
49 |
|
50 | ## Defining APIs
|
51 |
|
52 | APIs are defined as a hierarchy of resources and methods. `addResource` and
|
53 | `addMethod` can be used to build this hierarchy. The root resource is
|
54 | `api.root`.
|
55 |
|
56 | For example, the following code defines an API that includes the following HTTP
|
57 | endpoints: `ANY /`, `GET /books`, `POST /books`, `GET /books/{book_id}`, `DELETE /books/{book_id}`.
|
58 |
|
59 | ```ts
|
60 | const api = new apigateway.RestApi(this, 'books-api');
|
61 |
|
62 | api.root.addMethod('ANY');
|
63 |
|
64 | const books = api.root.addResource('books');
|
65 | books.addMethod('GET');
|
66 | books.addMethod('POST');
|
67 |
|
68 | const book = books.addResource('{book_id}');
|
69 | book.addMethod('GET');
|
70 | book.addMethod('DELETE');
|
71 | ```
|
72 |
|
73 | ## AWS Lambda-backed APIs
|
74 |
|
75 | A very common practice is to use Amazon API Gateway with AWS Lambda as the
|
76 | backend integration. The `LambdaRestApi` construct makes it easy:
|
77 |
|
78 | The following code defines a REST API that routes all requests to the
|
79 | specified AWS Lambda function:
|
80 |
|
81 | ```ts
|
82 | declare const backend: lambda.Function;
|
83 | new apigateway.LambdaRestApi(this, 'myapi', {
|
84 | handler: backend,
|
85 | });
|
86 | ```
|
87 |
|
88 | You can also supply `proxy: false`, in which case you will have to explicitly
|
89 | define the API model:
|
90 |
|
91 | ```ts
|
92 | declare const backend: lambda.Function;
|
93 | const api = new apigateway.LambdaRestApi(this, 'myapi', {
|
94 | handler: backend,
|
95 | proxy: false
|
96 | });
|
97 |
|
98 | const items = api.root.addResource('items');
|
99 | items.addMethod('GET'); // GET /items
|
100 | items.addMethod('POST'); // POST /items
|
101 |
|
102 | const item = items.addResource('{item}');
|
103 | item.addMethod('GET'); // GET /items/{item}
|
104 |
|
105 | // the default integration for methods is "handler", but one can
|
106 | // customize this behavior per method or even a sub path.
|
107 | item.addMethod('DELETE', new apigateway.HttpIntegration('http://amazon.com'));
|
108 | ```
|
109 |
|
110 | ## AWS StepFunctions backed APIs
|
111 |
|
112 | You can use Amazon API Gateway with AWS Step Functions as the backend integration, specifically Synchronous Express Workflows.
|
113 |
|
114 | The `StepFunctionsRestApi` only supports integration with Synchronous Express state machine. The `StepFunctionsRestApi` construct makes this easy by setting up input, output and error mapping.
|
115 |
|
116 | The construct sets up an API endpoint and maps the `ANY` HTTP method and any calls to the API endpoint starts an express workflow execution for the underlying state machine.
|
117 |
|
118 | Invoking the endpoint with any HTTP method (`GET`, `POST`, `PUT`, `DELETE`, ...) in the example below will send the request to the state machine as a new execution. On success, an HTTP code `200` is returned with the execution output as the Response Body.
|
119 |
|
120 | If the execution fails, an HTTP `500` response is returned with the `error` and `cause` from the execution output as the Response Body. If the request is invalid (ex. bad execution input) HTTP code `400` is returned.
|
121 |
|
122 | The response from the invocation contains only the `output` field from the
|
123 | [StartSyncExecution](https://docs.aws.amazon.com/step-functions/latest/apireference/API_StartSyncExecution.html#API_StartSyncExecution_ResponseSyntax) API.
|
124 | In case of failures, the fields `error` and `cause` are returned as part of the response.
|
125 | Other metadata such as billing details, AWS account ID and resource ARNs are not returned in the API response.
|
126 |
|
127 | By default, a `prod` stage is provisioned.
|
128 |
|
129 | In order to reduce the payload size sent to AWS Step Functions, `headers` are not forwarded to the Step Functions execution input. It is possible to choose whether `headers`, `requestContext`, `path`, `querystring`, and `authorizer` are included or not. By default, `headers` are excluded in all requests.
|
130 |
|
131 | More details about AWS Step Functions payload limit can be found at https://docs.aws.amazon.com/step-functions/latest/dg/limits-overview.html#service-limits-task-executions.
|
132 |
|
133 | The following code defines a REST API that routes all requests to the specified AWS StepFunctions state machine:
|
134 |
|
135 | ```ts
|
136 | const stateMachineDefinition = new stepfunctions.Pass(this, 'PassState');
|
137 |
|
138 | const stateMachine: stepfunctions.IStateMachine = new stepfunctions.StateMachine(this, 'StateMachine', {
|
139 | definition: stateMachineDefinition,
|
140 | stateMachineType: stepfunctions.StateMachineType.EXPRESS,
|
141 | });
|
142 |
|
143 | new apigateway.StepFunctionsRestApi(this, 'StepFunctionsRestApi', {
|
144 | deploy: true,
|
145 | stateMachine: stateMachine,
|
146 | });
|
147 | ```
|
148 |
|
149 | When the REST API endpoint configuration above is invoked using POST, as follows -
|
150 |
|
151 | ```bash
|
152 | curl -X POST -d '{ "customerId": 1 }' https://example.com/
|
153 | ```
|
154 |
|
155 | AWS Step Functions will receive the request body in its input as follows:
|
156 |
|
157 | ```json
|
158 | {
|
159 | "body": {
|
160 | "customerId": 1
|
161 | },
|
162 | "path": "/",
|
163 | "querystring": {}
|
164 | }
|
165 | ```
|
166 |
|
167 | When the endpoint is invoked at path '/users/5' using the HTTP GET method as below:
|
168 |
|
169 | ```bash
|
170 | curl -X GET https://example.com/users/5?foo=bar
|
171 | ```
|
172 |
|
173 | AWS Step Functions will receive the following execution input:
|
174 |
|
175 | ```json
|
176 | {
|
177 | "body": {},
|
178 | "path": {
|
179 | "users": "5"
|
180 | },
|
181 | "querystring": {
|
182 | "foo": "bar"
|
183 | }
|
184 | }
|
185 | ```
|
186 |
|
187 | Additional information around the request such as the request context, authorizer context, and headers can be included as part of the input
|
188 | forwarded to the state machine. The following example enables headers to be included in the input but not query string.
|
189 |
|
190 | ```ts fixture=stepfunctions
|
191 | new apigateway.StepFunctionsRestApi(this, 'StepFunctionsRestApi', {
|
192 | stateMachine: machine,
|
193 | headers: true,
|
194 | path: false,
|
195 | querystring: false,
|
196 | authorizer: false,
|
197 | requestContext: {
|
198 | caller: true,
|
199 | user: true,
|
200 | },
|
201 | });
|
202 | ```
|
203 |
|
204 | In such a case, when the endpoint is invoked as below:
|
205 |
|
206 | ```bash
|
207 | curl -X GET https://example.com/
|
208 | ```
|
209 |
|
210 | AWS Step Functions will receive the following execution input:
|
211 |
|
212 | ```json
|
213 | {
|
214 | "headers": {
|
215 | "Accept": "...",
|
216 | "CloudFront-Forwarded-Proto": "...",
|
217 | },
|
218 | "requestContext": {
|
219 | "accountId": "...",
|
220 | "apiKey": "...",
|
221 | },
|
222 | "body": {}
|
223 | }
|
224 | ```
|
225 |
|
226 | ### Breaking up Methods and Resources across Stacks
|
227 |
|
228 | It is fairly common for REST APIs with a large number of Resources and Methods to hit the [CloudFormation
|
229 | limit](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html) of 500 resources per
|
230 | stack.
|
231 |
|
232 | To help with this, Resources and Methods for the same REST API can be re-organized across multiple stacks. A common
|
233 | way to do this is to have a stack per Resource or groups of Resources, but this is not the only possible way.
|
234 | The following example uses sets up two Resources '/pets' and '/books' in separate stacks using nested stacks:
|
235 |
|
236 | [Resources grouped into nested stacks](test/integ.restapi-import.lit.ts)
|
237 |
|
238 | ## Integration Targets
|
239 |
|
240 | Methods are associated with backend integrations, which are invoked when this
|
241 | method is called. API Gateway supports the following integrations:
|
242 |
|
243 | - `MockIntegration` - can be used to test APIs. This is the default
|
244 | integration if one is not specified.
|
245 | - `LambdaIntegration` - can be used to invoke an AWS Lambda function.
|
246 | - `AwsIntegration` - can be used to invoke arbitrary AWS service APIs.
|
247 | - `HttpIntegration` - can be used to invoke HTTP endpoints.
|
248 |
|
249 | The following example shows how to integrate the `GET /book/{book_id}` method to
|
250 | an AWS Lambda function:
|
251 |
|
252 | ```ts
|
253 | declare const getBookHandler: lambda.Function;
|
254 | declare const book: apigateway.Resource;
|
255 |
|
256 | const getBookIntegration = new apigateway.LambdaIntegration(getBookHandler);
|
257 | book.addMethod('GET', getBookIntegration);
|
258 | ```
|
259 |
|
260 | Integration options can be optionally be specified:
|
261 |
|
262 | ```ts
|
263 | declare const getBookHandler: lambda.Function;
|
264 | declare const getBookIntegration: apigateway.LambdaIntegration;
|
265 |
|
266 | const getBookIntegration = new apigateway.LambdaIntegration(getBookHandler, {
|
267 | contentHandling: apigateway.ContentHandling.CONVERT_TO_TEXT, // convert to base64
|
268 | credentialsPassthrough: true, // use caller identity to invoke the function
|
269 | });
|
270 | ```
|
271 |
|
272 | Method options can optionally be specified when adding methods:
|
273 |
|
274 | ```ts
|
275 | declare const book: apigateway.Resource;
|
276 | declare const getBookIntegration: apigateway.LambdaIntegration;
|
277 |
|
278 | book.addMethod('GET', getBookIntegration, {
|
279 | authorizationType: apigateway.AuthorizationType.IAM,
|
280 | apiKeyRequired: true
|
281 | });
|
282 | ```
|
283 |
|
284 | It is possible to also integrate with AWS services in a different region. The following code integrates with Amazon SQS in the
|
285 | `eu-west-1` region.
|
286 |
|
287 | ```ts
|
288 | const getMessageIntegration = new apigateway.AwsIntegration({
|
289 | service: 'sqs',
|
290 | path: 'queueName',
|
291 | region: 'eu-west-1'
|
292 | });
|
293 | ```
|
294 |
|
295 | ## Usage Plan & API Keys
|
296 |
|
297 | A usage plan specifies who can access one or more deployed API stages and methods, and the rate at which they can be
|
298 | accessed. The plan uses API keys to identify API clients and meters access to the associated API stages for each key.
|
299 | Usage plans also allow configuring throttling limits and quota limits that are enforced on individual client API keys.
|
300 |
|
301 | The following example shows how to create and asscociate a usage plan and an API key:
|
302 |
|
303 | ```ts
|
304 | declare const integration: apigateway.LambdaIntegration;
|
305 |
|
306 | const api = new apigateway.RestApi(this, 'hello-api');
|
307 |
|
308 | const v1 = api.root.addResource('v1');
|
309 | const echo = v1.addResource('echo');
|
310 | const echoMethod = echo.addMethod('GET', integration, { apiKeyRequired: true });
|
311 |
|
312 | const plan = api.addUsagePlan('UsagePlan', {
|
313 | name: 'Easy',
|
314 | throttle: {
|
315 | rateLimit: 10,
|
316 | burstLimit: 2
|
317 | }
|
318 | });
|
319 |
|
320 | const key = api.addApiKey('ApiKey');
|
321 | plan.addApiKey(key);
|
322 | ```
|
323 |
|
324 | To associate a plan to a given RestAPI stage:
|
325 |
|
326 | ```ts
|
327 | declare const plan: apigateway.UsagePlan;
|
328 | declare const api: apigateway.RestApi;
|
329 | declare const echoMethod: apigateway.Method;
|
330 |
|
331 | plan.addApiStage({
|
332 | stage: api.deploymentStage,
|
333 | throttle: [
|
334 | {
|
335 | method: echoMethod,
|
336 | throttle: {
|
337 | rateLimit: 10,
|
338 | burstLimit: 2
|
339 | }
|
340 | }
|
341 | ]
|
342 | });
|
343 | ```
|
344 |
|
345 | Existing usage plans can be imported into a CDK app using its id.
|
346 |
|
347 | ```ts
|
348 | const importedUsagePlan = apigateway.UsagePlan.fromUsagePlanId(this, 'imported-usage-plan', '<usage-plan-key-id>');
|
349 | ```
|
350 |
|
351 | The name and value of the API Key can be specified at creation; if not
|
352 | provided, a name and value will be automatically generated by API Gateway.
|
353 |
|
354 | ```ts
|
355 | declare const api: apigateway.RestApi;
|
356 | const key = api.addApiKey('ApiKey', {
|
357 | apiKeyName: 'myApiKey1',
|
358 | value: 'MyApiKeyThatIsAtLeast20Characters',
|
359 | });
|
360 | ```
|
361 |
|
362 | Existing API keys can also be imported into a CDK app using its id.
|
363 |
|
364 | ```ts
|
365 | const importedKey = apigateway.ApiKey.fromApiKeyId(this, 'imported-key', '<api-key-id>');
|
366 | ```
|
367 |
|
368 | The "grant" methods can be used to give prepackaged sets of permissions to other resources. The
|
369 | following code provides read permission to an API key.
|
370 |
|
371 | ```ts
|
372 | declare const importedKey: apigateway.ApiKey;
|
373 | declare const lambdaFn: lambda.Function;
|
374 | importedKey.grantRead(lambdaFn);
|
375 | ```
|
376 |
|
377 | ### ⚠️ Multiple API Keys
|
378 |
|
379 | It is possible to specify multiple API keys for a given Usage Plan, by calling `usagePlan.addApiKey()`.
|
380 |
|
381 | When using multiple API keys, a past bug of the CDK prevents API key associations to a Usage Plan to be deleted.
|
382 | If the CDK app had the [feature flag] - `@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId` - enabled when the API
|
383 | keys were created, then the app will not be affected by this bug.
|
384 |
|
385 | If this is not the case, you will need to ensure that the CloudFormation [logical ids] of the API keys that are not
|
386 | being deleted remain unchanged.
|
387 | Make note of the logical ids of these API keys before removing any, and set it as part of the `addApiKey()` method:
|
388 |
|
389 | ```ts
|
390 | declare const usageplan: apigateway.UsagePlan;
|
391 | declare const apiKey: apigateway.ApiKey;
|
392 |
|
393 | usageplan.addApiKey(apiKey, {
|
394 | overrideLogicalId: '...',
|
395 | });
|
396 | ```
|
397 |
|
398 | [feature flag]: https://docs.aws.amazon.com/cdk/latest/guide/featureflags.html
|
399 | [logical ids]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/resources-section-structure.html
|
400 |
|
401 | ### Rate Limited API Key
|
402 |
|
403 | In scenarios where you need to create a single api key and configure rate limiting for it, you can use `RateLimitedApiKey`.
|
404 | This construct lets you specify rate limiting properties which should be applied only to the api key being created.
|
405 | The API key created has the specified rate limits, such as quota and throttles, applied.
|
406 |
|
407 | The following example shows how to use a rate limited api key :
|
408 |
|
409 | ```ts
|
410 | declare const api: apigateway.RestApi;
|
411 |
|
412 | const key = new apigateway.RateLimitedApiKey(this, 'rate-limited-api-key', {
|
413 | customerId: 'hello-customer',
|
414 | resources: [api],
|
415 | quota: {
|
416 | limit: 10000,
|
417 | period: apigateway.Period.MONTH
|
418 | }
|
419 | });
|
420 | ```
|
421 |
|
422 | ## Working with models
|
423 |
|
424 | When you work with Lambda integrations that are not Proxy integrations, you
|
425 | have to define your models and mappings for the request, response, and integration.
|
426 |
|
427 | ```ts
|
428 | const hello = new lambda.Function(this, 'hello', {
|
429 | runtime: lambda.Runtime.NODEJS_12_X,
|
430 | handler: 'hello.handler',
|
431 | code: lambda.Code.fromAsset('lambda')
|
432 | });
|
433 |
|
434 | const api = new apigateway.RestApi(this, 'hello-api', { });
|
435 | const resource = api.root.addResource('v1');
|
436 | ```
|
437 |
|
438 | You can define more parameters on the integration to tune the behavior of API Gateway
|
439 |
|
440 | ```ts
|
441 | declare const hello: lambda.Function;
|
442 |
|
443 | const integration = new apigateway.LambdaIntegration(hello, {
|
444 | proxy: false,
|
445 | requestParameters: {
|
446 | // You can define mapping parameters from your method to your integration
|
447 | // - Destination parameters (the key) are the integration parameters (used in mappings)
|
448 | // - Source parameters (the value) are the source request parameters or expressions
|
449 | // @see: https://docs.aws.amazon.com/apigateway/latest/developerguide/request-response-data-mappings.html
|
450 | 'integration.request.querystring.who': 'method.request.querystring.who'
|
451 | },
|
452 | allowTestInvoke: true,
|
453 | requestTemplates: {
|
454 | // You can define a mapping that will build a payload for your integration, based
|
455 | // on the integration parameters that you have specified
|
456 | // Check: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
|
457 | 'application/json': JSON.stringify({ action: 'sayHello', pollId: "$util.escapeJavaScript($input.params('who'))" })
|
458 | },
|
459 | // This parameter defines the behavior of the engine is no suitable response template is found
|
460 | passthroughBehavior: apigateway.PassthroughBehavior.NEVER,
|
461 | integrationResponses: [
|
462 | {
|
463 | // Successful response from the Lambda function, no filter defined
|
464 | // - the selectionPattern filter only tests the error message
|
465 | // We will set the response status code to 200
|
466 | statusCode: "200",
|
467 | responseTemplates: {
|
468 | // This template takes the "message" result from the Lambda function, and embeds it in a JSON response
|
469 | // Check https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
|
470 | 'application/json': JSON.stringify({ state: 'ok', greeting: '$util.escapeJavaScript($input.body)' })
|
471 | },
|
472 | responseParameters: {
|
473 | // We can map response parameters
|
474 | // - Destination parameters (the key) are the response parameters (used in mappings)
|
475 | // - Source parameters (the value) are the integration response parameters or expressions
|
476 | 'method.response.header.Content-Type': "'application/json'",
|
477 | 'method.response.header.Access-Control-Allow-Origin': "'*'",
|
478 | 'method.response.header.Access-Control-Allow-Credentials': "'true'"
|
479 | }
|
480 | },
|
481 | {
|
482 | // For errors, we check if the error message is not empty, get the error data
|
483 | selectionPattern: '(\n|.)+',
|
484 | // We will set the response status code to 200
|
485 | statusCode: "400",
|
486 | responseTemplates: {
|
487 | 'application/json': JSON.stringify({ state: 'error', message: "$util.escapeJavaScript($input.path('$.errorMessage'))" })
|
488 | },
|
489 | responseParameters: {
|
490 | 'method.response.header.Content-Type': "'application/json'",
|
491 | 'method.response.header.Access-Control-Allow-Origin': "'*'",
|
492 | 'method.response.header.Access-Control-Allow-Credentials': "'true'"
|
493 | }
|
494 | }
|
495 | ]
|
496 | });
|
497 |
|
498 | ```
|
499 |
|
500 | You can define models for your responses (and requests)
|
501 |
|
502 | ```ts
|
503 | declare const api: apigateway.RestApi;
|
504 |
|
505 | // We define the JSON Schema for the transformed valid response
|
506 | const responseModel = api.addModel('ResponseModel', {
|
507 | contentType: 'application/json',
|
508 | modelName: 'ResponseModel',
|
509 | schema: {
|
510 | schema: apigateway.JsonSchemaVersion.DRAFT4,
|
511 | title: 'pollResponse',
|
512 | type: apigateway.JsonSchemaType.OBJECT,
|
513 | properties: {
|
514 | state: { type: apigateway.JsonSchemaType.STRING },
|
515 | greeting: { type: apigateway.JsonSchemaType.STRING }
|
516 | }
|
517 | }
|
518 | });
|
519 |
|
520 | // We define the JSON Schema for the transformed error response
|
521 | const errorResponseModel = api.addModel('ErrorResponseModel', {
|
522 | contentType: 'application/json',
|
523 | modelName: 'ErrorResponseModel',
|
524 | schema: {
|
525 | schema: apigateway.JsonSchemaVersion.DRAFT4,
|
526 | title: 'errorResponse',
|
527 | type: apigateway.JsonSchemaType.OBJECT,
|
528 | properties: {
|
529 | state: { type: apigateway.JsonSchemaType.STRING },
|
530 | message: { type: apigateway.JsonSchemaType.STRING }
|
531 | }
|
532 | }
|
533 | });
|
534 |
|
535 | ```
|
536 |
|
537 | And reference all on your method definition.
|
538 |
|
539 | ```ts
|
540 | declare const integration: apigateway.LambdaIntegration;
|
541 | declare const resource: apigateway.Resource;
|
542 | declare const responseModel: apigateway.Model;
|
543 | declare const errorResponseModel: apigateway.Model;
|
544 |
|
545 | resource.addMethod('GET', integration, {
|
546 | // We can mark the parameters as required
|
547 | requestParameters: {
|
548 | 'method.request.querystring.who': true
|
549 | },
|
550 | // we can set request validator options like below
|
551 | requestValidatorOptions: {
|
552 | requestValidatorName: 'test-validator',
|
553 | validateRequestBody: true,
|
554 | validateRequestParameters: false
|
555 | },
|
556 | methodResponses: [
|
557 | {
|
558 | // Successful response from the integration
|
559 | statusCode: '200',
|
560 | // Define what parameters are allowed or not
|
561 | responseParameters: {
|
562 | 'method.response.header.Content-Type': true,
|
563 | 'method.response.header.Access-Control-Allow-Origin': true,
|
564 | 'method.response.header.Access-Control-Allow-Credentials': true
|
565 | },
|
566 | // Validate the schema on the response
|
567 | responseModels: {
|
568 | 'application/json': responseModel
|
569 | }
|
570 | },
|
571 | {
|
572 | // Same thing for the error responses
|
573 | statusCode: '400',
|
574 | responseParameters: {
|
575 | 'method.response.header.Content-Type': true,
|
576 | 'method.response.header.Access-Control-Allow-Origin': true,
|
577 | 'method.response.header.Access-Control-Allow-Credentials': true
|
578 | },
|
579 | responseModels: {
|
580 | 'application/json': errorResponseModel
|
581 | }
|
582 | }
|
583 | ]
|
584 | });
|
585 | ```
|
586 |
|
587 | Specifying `requestValidatorOptions` automatically creates the RequestValidator construct with the given options.
|
588 | However, if you have your RequestValidator already initialized or imported, use the `requestValidator` option instead.
|
589 |
|
590 | ## Default Integration and Method Options
|
591 |
|
592 | The `defaultIntegration` and `defaultMethodOptions` properties can be used to
|
593 | configure a default integration at any resource level. These options will be
|
594 | used when defining method under this resource (recursively) with undefined
|
595 | integration or options.
|
596 |
|
597 | > If not defined, the default integration is `MockIntegration`. See reference
|
598 | documentation for default method options.
|
599 |
|
600 | The following example defines the `booksBackend` integration as a default
|
601 | integration. This means that all API methods that do not explicitly define an
|
602 | integration will be routed to this AWS Lambda function.
|
603 |
|
604 | ```ts
|
605 | declare const booksBackend: apigateway.LambdaIntegration;
|
606 | const api = new apigateway.RestApi(this, 'books', {
|
607 | defaultIntegration: booksBackend
|
608 | });
|
609 |
|
610 | const books = api.root.addResource('books');
|
611 | books.addMethod('GET'); // integrated with `booksBackend`
|
612 | books.addMethod('POST'); // integrated with `booksBackend`
|
613 |
|
614 | const book = books.addResource('{book_id}');
|
615 | book.addMethod('GET'); // integrated with `booksBackend`
|
616 | ```
|
617 |
|
618 | A Method can be configured with authorization scopes. Authorization scopes are
|
619 | used in conjunction with an [authorizer that uses Amazon Cognito user
|
620 | pools](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-integrate-with-cognito.html#apigateway-enable-cognito-user-pool).
|
621 | Read more about authorization scopes
|
622 | [here](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-method.html#cfn-apigateway-method-authorizationscopes).
|
623 |
|
624 | Authorization scopes for a Method can be configured using the `authorizationScopes` property as shown below -
|
625 |
|
626 | ```ts
|
627 | declare const books: apigateway.Resource;
|
628 |
|
629 | books.addMethod('GET', new apigateway.HttpIntegration('http://amazon.com'), {
|
630 | authorizationType: apigateway.AuthorizationType.COGNITO,
|
631 | authorizationScopes: ['Scope1','Scope2']
|
632 | });
|
633 | ```
|
634 |
|
635 | ## Proxy Routes
|
636 |
|
637 | The `addProxy` method can be used to install a greedy `{proxy+}` resource
|
638 | on a path. By default, this also installs an `"ANY"` method:
|
639 |
|
640 | ```ts
|
641 | declare const resource: apigateway.Resource;
|
642 | declare const handler: lambda.Function;
|
643 | const proxy = resource.addProxy({
|
644 | defaultIntegration: new apigateway.LambdaIntegration(handler),
|
645 |
|
646 | // "false" will require explicitly adding methods on the `proxy` resource
|
647 | anyMethod: true // "true" is the default
|
648 | });
|
649 | ```
|
650 |
|
651 | ## Authorizers
|
652 |
|
653 | API Gateway [supports several different authorization types](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-control-access-to-api.html)
|
654 | that can be used for controlling access to your REST APIs.
|
655 |
|
656 | ### IAM-based authorizer
|
657 |
|
658 | The following CDK code provides 'execute-api' permission to an IAM user, via IAM policies, for the 'GET' method on the `books` resource:
|
659 |
|
660 | ```ts
|
661 | declare const books: apigateway.Resource;
|
662 | declare const iamUser: iam.User;
|
663 |
|
664 | const getBooks = books.addMethod('GET', new apigateway.HttpIntegration('http://amazon.com'), {
|
665 | authorizationType: apigateway.AuthorizationType.IAM
|
666 | });
|
667 |
|
668 | iamUser.attachInlinePolicy(new iam.Policy(this, 'AllowBooks', {
|
669 | statements: [
|
670 | new iam.PolicyStatement({
|
671 | actions: [ 'execute-api:Invoke' ],
|
672 | effect: iam.Effect.ALLOW,
|
673 | resources: [ getBooks.methodArn ]
|
674 | })
|
675 | ]
|
676 | }))
|
677 | ```
|
678 |
|
679 | ### Lambda-based token authorizer
|
680 |
|
681 | API Gateway also allows [lambda functions to be used as authorizers](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html).
|
682 |
|
683 | This module provides support for token-based Lambda authorizers. When a client makes a request to an API's methods configured with such
|
684 | an authorizer, API Gateway calls the Lambda authorizer, which takes the caller's identity as input and returns an IAM policy as output.
|
685 | A token-based Lambda authorizer (also called a token authorizer) receives the caller's identity in a bearer token, such as
|
686 | a JSON Web Token (JWT) or an OAuth token.
|
687 |
|
688 | API Gateway interacts with the authorizer Lambda function handler by passing input and expecting the output in a specific format.
|
689 | The event object that the handler is called with contains the `authorizationToken` and the `methodArn` from the request to the
|
690 | API Gateway endpoint. The handler is expected to return the `principalId` (i.e. the client identifier) and a `policyDocument` stating
|
691 | what the client is authorizer to perform.
|
692 | See [here](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html) for a detailed specification on
|
693 | inputs and outputs of the Lambda handler.
|
694 |
|
695 | The following code attaches a token-based Lambda authorizer to the 'GET' Method of the Book resource:
|
696 |
|
697 | ```ts
|
698 | declare const authFn: lambda.Function;
|
699 | declare const books: apigateway.Resource;
|
700 |
|
701 | const auth = new apigateway.TokenAuthorizer(this, 'booksAuthorizer', {
|
702 | handler: authFn
|
703 | });
|
704 |
|
705 | books.addMethod('GET', new apigateway.HttpIntegration('http://amazon.com'), {
|
706 | authorizer: auth
|
707 | });
|
708 | ```
|
709 |
|
710 | A full working example is shown below.
|
711 |
|
712 | [Full token authorizer example](test/authorizers/integ.token-authorizer.lit.ts).
|
713 |
|
714 | By default, the `TokenAuthorizer` looks for the authorization token in the request header with the key 'Authorization'. This can,
|
715 | however, be modified by changing the `identitySource` property.
|
716 |
|
717 | Authorizers can also be passed via the `defaultMethodOptions` property within the `RestApi` construct or the `Method` construct. Unless
|
718 | explicitly overridden, the specified defaults will be applied across all `Method`s across the `RestApi` or across all `Resource`s,
|
719 | depending on where the defaults were specified.
|
720 |
|
721 | ### Lambda-based request authorizer
|
722 |
|
723 | This module provides support for request-based Lambda authorizers. When a client makes a request to an API's methods configured with such
|
724 | an authorizer, API Gateway calls the Lambda authorizer, which takes specified parts of the request, known as identity sources,
|
725 | as input and returns an IAM policy as output. A request-based Lambda authorizer (also called a request authorizer) receives
|
726 | the identity sources in a series of values pulled from the request, from the headers, stage variables, query strings, and the context.
|
727 |
|
728 | API Gateway interacts with the authorizer Lambda function handler by passing input and expecting the output in a specific format.
|
729 | The event object that the handler is called with contains the body of the request and the `methodArn` from the request to the
|
730 | API Gateway endpoint. The handler is expected to return the `principalId` (i.e. the client identifier) and a `policyDocument` stating
|
731 | what the client is authorizer to perform.
|
732 | See [here](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html) for a detailed specification on
|
733 | inputs and outputs of the Lambda handler.
|
734 |
|
735 | The following code attaches a request-based Lambda authorizer to the 'GET' Method of the Book resource:
|
736 |
|
737 | ```ts
|
738 | declare const authFn: lambda.Function;
|
739 | declare const books: apigateway.Resource;
|
740 |
|
741 | const auth = new apigateway.RequestAuthorizer(this, 'booksAuthorizer', {
|
742 | handler: authFn,
|
743 | identitySources: [apigateway.IdentitySource.header('Authorization')]
|
744 | });
|
745 |
|
746 | books.addMethod('GET', new apigateway.HttpIntegration('http://amazon.com'), {
|
747 | authorizer: auth
|
748 | });
|
749 | ```
|
750 |
|
751 | A full working example is shown below.
|
752 |
|
753 | [Full request authorizer example](test/authorizers/integ.request-authorizer.lit.ts).
|
754 |
|
755 | By default, the `RequestAuthorizer` does not pass any kind of information from the request. This can,
|
756 | however, be modified by changing the `identitySource` property, and is required when specifying a value for caching.
|
757 |
|
758 | Authorizers can also be passed via the `defaultMethodOptions` property within the `RestApi` construct or the `Method` construct. Unless
|
759 | explicitly overridden, the specified defaults will be applied across all `Method`s across the `RestApi` or across all `Resource`s,
|
760 | depending on where the defaults were specified.
|
761 |
|
762 | ### Cognito User Pools authorizer
|
763 |
|
764 | API Gateway also allows [Amazon Cognito user pools as authorizer](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-integrate-with-cognito.html)
|
765 |
|
766 | The following snippet configures a Cognito user pool as an authorizer:
|
767 |
|
768 | ```ts
|
769 | const userPool = new cognito.UserPool(this, 'UserPool');
|
770 |
|
771 | const auth = new apigateway.CognitoUserPoolsAuthorizer(this, 'booksAuthorizer', {
|
772 | cognitoUserPools: [userPool]
|
773 | });
|
774 |
|
775 | declare const books: apigateway.Resource;
|
776 | books.addMethod('GET', new apigateway.HttpIntegration('http://amazon.com'), {
|
777 | authorizer: auth,
|
778 | authorizationType: apigateway.AuthorizationType.COGNITO,
|
779 | });
|
780 | ```
|
781 |
|
782 | ## Mutual TLS (mTLS)
|
783 |
|
784 | Mutual TLS can be configured to limit access to your API based by using client certificates instead of (or as an extension of) using authorization headers.
|
785 |
|
786 | ```ts
|
787 | declare const acm: any;
|
788 |
|
789 | new apigateway.DomainName(this, 'domain-name', {
|
790 | domainName: 'example.com',
|
791 | certificate: acm.Certificate.fromCertificateArn(this, 'cert', 'arn:aws:acm:us-east-1:1111111:certificate/11-3336f1-44483d-adc7-9cd375c5169d'),
|
792 | mtls: {
|
793 | bucket: new s3.Bucket(this, 'bucket'),
|
794 | key: 'truststore.pem',
|
795 | version: 'version',
|
796 | },
|
797 | });
|
798 | ```
|
799 |
|
800 | Instructions for configuring your trust store can be found [here](https://aws.amazon.com/blogs/compute/introducing-mutual-tls-authentication-for-amazon-api-gateway/).
|
801 |
|
802 | ## Deployments
|
803 |
|
804 | By default, the `RestApi` construct will automatically create an API Gateway
|
805 | [Deployment] and a "prod" [Stage] which represent the API configuration you
|
806 | defined in your CDK app. This means that when you deploy your app, your API will
|
807 | be have open access from the internet via the stage URL.
|
808 |
|
809 | The URL of your API can be obtained from the attribute `restApi.url`, and is
|
810 | also exported as an `Output` from your stack, so it's printed when you `cdk
|
811 | deploy` your app:
|
812 |
|
813 | ```console
|
814 | $ cdk deploy
|
815 | ...
|
816 | books.booksapiEndpointE230E8D5 = https://6lyktd4lpk.execute-api.us-east-1.amazonaws.com/prod/
|
817 | ```
|
818 |
|
819 | To disable this behavior, you can set `{ deploy: false }` when creating your
|
820 | API. This means that the API will not be deployed and a stage will not be
|
821 | created for it. You will need to manually define a `apigateway.Deployment` and
|
822 | `apigateway.Stage` resources.
|
823 |
|
824 | Use the `deployOptions` property to customize the deployment options of your
|
825 | API.
|
826 |
|
827 | The following example will configure API Gateway to emit logs and data traces to
|
828 | AWS CloudWatch for all API calls:
|
829 |
|
830 | > By default, an IAM role will be created and associated with API Gateway to
|
831 | allow it to write logs and metrics to AWS CloudWatch unless `cloudWatchRole` is
|
832 | set to `false`.
|
833 |
|
834 | ```ts
|
835 | const api = new apigateway.RestApi(this, 'books', {
|
836 | deployOptions: {
|
837 | loggingLevel: apigateway.MethodLoggingLevel.INFO,
|
838 | dataTraceEnabled: true
|
839 | }
|
840 | })
|
841 | ```
|
842 |
|
843 | ### Deep dive: Invalidation of deployments
|
844 |
|
845 | API Gateway deployments are an immutable snapshot of the API. This means that we
|
846 | want to automatically create a new deployment resource every time the API model
|
847 | defined in our CDK app changes.
|
848 |
|
849 | In order to achieve that, the AWS CloudFormation logical ID of the
|
850 | `AWS::ApiGateway::Deployment` resource is dynamically calculated by hashing the
|
851 | API configuration (resources, methods). This means that when the configuration
|
852 | changes (i.e. a resource or method are added, configuration is changed), a new
|
853 | logical ID will be assigned to the deployment resource. This will cause
|
854 | CloudFormation to create a new deployment resource.
|
855 |
|
856 | By default, old deployments are _deleted_. You can set `retainDeployments: true`
|
857 | to allow users revert the stage to an old deployment manually.
|
858 |
|
859 | [Deployment]: https://docs.aws.amazon.com/apigateway/api-reference/resource/deployment/
|
860 | [Stage]: https://docs.aws.amazon.com/apigateway/api-reference/resource/stage/
|
861 |
|
862 | ## Custom Domains
|
863 |
|
864 | To associate an API with a custom domain, use the `domainName` configuration when
|
865 | you define your API:
|
866 |
|
867 | ```ts
|
868 | declare const acmCertificateForExampleCom: any;
|
869 |
|
870 | const api = new apigateway.RestApi(this, 'MyDomain', {
|
871 | domainName: {
|
872 | domainName: 'example.com',
|
873 | certificate: acmCertificateForExampleCom,
|
874 | },
|
875 | });
|
876 | ```
|
877 |
|
878 | This will define a `DomainName` resource for you, along with a `BasePathMapping`
|
879 | from the root of the domain to the deployment stage of the API. This is a common
|
880 | set up.
|
881 |
|
882 | To route domain traffic to an API Gateway API, use Amazon Route 53 to create an
|
883 | alias record. An alias record is a Route 53 extension to DNS. It's similar to a
|
884 | CNAME record, but you can create an alias record both for the root domain, such
|
885 | as `example.com`, and for subdomains, such as `www.example.com`. (You can create
|
886 | CNAME records only for subdomains.)
|
887 |
|
888 | ```ts
|
889 | import * as route53 from '@aws-cdk/aws-route53';
|
890 | import * as targets from '@aws-cdk/aws-route53-targets';
|
891 |
|
892 | declare const api: apigateway.RestApi;
|
893 | declare const hostedZoneForExampleCom: any;
|
894 |
|
895 | new route53.ARecord(this, 'CustomDomainAliasRecord', {
|
896 | zone: hostedZoneForExampleCom,
|
897 | target: route53.RecordTarget.fromAlias(new targets.ApiGateway(api))
|
898 | });
|
899 | ```
|
900 |
|
901 | You can also define a `DomainName` resource directly in order to customize the default behavior:
|
902 |
|
903 | ```ts
|
904 | declare const acmCertificateForExampleCom: any;
|
905 |
|
906 | new apigateway.DomainName(this, 'custom-domain', {
|
907 | domainName: 'example.com',
|
908 | certificate: acmCertificateForExampleCom,
|
909 | endpointType: apigateway.EndpointType.EDGE, // default is REGIONAL
|
910 | securityPolicy: apigateway.SecurityPolicy.TLS_1_2
|
911 | });
|
912 | ```
|
913 |
|
914 | Once you have a domain, you can map base paths of the domain to APIs.
|
915 | The following example will map the URL <https://example.com/go-to-api1>
|
916 | to the `api1` API and <https://example.com/boom> to the `api2` API.
|
917 |
|
918 | ```ts
|
919 | declare const domain: apigateway.DomainName;
|
920 | declare const api1: apigateway.RestApi;
|
921 | declare const api2: apigateway.RestApi;
|
922 |
|
923 | domain.addBasePathMapping(api1, { basePath: 'go-to-api1' });
|
924 | domain.addBasePathMapping(api2, { basePath: 'boom' });
|
925 | ```
|
926 |
|
927 | You can specify the API `Stage` to which this base path URL will map to. By default, this will be the
|
928 | `deploymentStage` of the `RestApi`.
|
929 |
|
930 | ```ts
|
931 | declare const domain: apigateway.DomainName;
|
932 | declare const restapi: apigateway.RestApi;
|
933 |
|
934 | const betaDeploy = new apigateway.Deployment(this, 'beta-deployment', {
|
935 | api: restapi,
|
936 | });
|
937 | const betaStage = new apigateway.Stage(this, 'beta-stage', {
|
938 | deployment: betaDeploy,
|
939 | });
|
940 | domain.addBasePathMapping(restapi, { basePath: 'api/beta', stage: betaStage });
|
941 | ```
|
942 |
|
943 | If you don't specify `basePath`, all URLs under this domain will be mapped
|
944 | to the API, and you won't be able to map another API to the same domain:
|
945 |
|
946 | ```ts
|
947 | declare const domain: apigateway.DomainName;
|
948 | declare const api: apigateway.RestApi;
|
949 | domain.addBasePathMapping(api);
|
950 | ```
|
951 |
|
952 | This can also be achieved through the `mapping` configuration when defining the
|
953 | domain as demonstrated above.
|
954 |
|
955 | If you wish to setup this domain with an Amazon Route53 alias, use the `targets.ApiGatewayDomain`:
|
956 |
|
957 | ```ts
|
958 | declare const hostedZoneForExampleCom: any;
|
959 | declare const domainName: apigateway.DomainName;
|
960 |
|
961 | import * as route53 from '@aws-cdk/aws-route53';
|
962 | import * as targets from '@aws-cdk/aws-route53-targets';
|
963 |
|
964 | new route53.ARecord(this, 'CustomDomainAliasRecord', {
|
965 | zone: hostedZoneForExampleCom,
|
966 | target: route53.RecordTarget.fromAlias(new targets.ApiGatewayDomain(domainName))
|
967 | });
|
968 | ```
|
969 |
|
970 | ## Access Logging
|
971 |
|
972 | Access logging creates logs every time an API method is accessed. Access logs can have information on
|
973 | who has accessed the API, how the caller accessed the API and what responses were generated.
|
974 | Access logs are configured on a Stage of the RestApi.
|
975 | Access logs can be expressed in a format of your choosing, and can contain any access details, with a
|
976 | minimum that it must include the 'requestId'. The list of variables that can be expressed in the access
|
977 | log can be found
|
978 | [here](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html#context-variable-reference).
|
979 | Read more at [Setting Up CloudWatch API Logging in API
|
980 | Gateway](https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-logging.html)
|
981 |
|
982 | ```ts
|
983 | // production stage
|
984 | const prdLogGroup = new logs.LogGroup(this, "PrdLogs");
|
985 | const api = new apigateway.RestApi(this, 'books', {
|
986 | deployOptions: {
|
987 | accessLogDestination: new apigateway.LogGroupLogDestination(prdLogGroup),
|
988 | accessLogFormat: apigateway.AccessLogFormat.jsonWithStandardFields()
|
989 | }
|
990 | })
|
991 | const deployment = new apigateway.Deployment(this, 'Deployment', {api});
|
992 |
|
993 | // development stage
|
994 | const devLogGroup = new logs.LogGroup(this, "DevLogs");
|
995 | new apigateway.Stage(this, 'dev', {
|
996 | deployment,
|
997 | accessLogDestination: new apigateway.LogGroupLogDestination(devLogGroup),
|
998 | accessLogFormat: apigateway.AccessLogFormat.jsonWithStandardFields({
|
999 | caller: false,
|
1000 | httpMethod: true,
|
1001 | ip: true,
|
1002 | protocol: true,
|
1003 | requestTime: true,
|
1004 | resourcePath: true,
|
1005 | responseLength: true,
|
1006 | status: true,
|
1007 | user: true
|
1008 | })
|
1009 | });
|
1010 | ```
|
1011 |
|
1012 | The following code will generate the access log in the [CLF format](https://en.wikipedia.org/wiki/Common_Log_Format).
|
1013 |
|
1014 | ```ts
|
1015 | const logGroup = new logs.LogGroup(this, "ApiGatewayAccessLogs");
|
1016 | const api = new apigateway.RestApi(this, 'books', {
|
1017 | deployOptions: {
|
1018 | accessLogDestination: new apigateway.LogGroupLogDestination(logGroup),
|
1019 | accessLogFormat: apigateway.AccessLogFormat.clf(),
|
1020 | }});
|
1021 | ```
|
1022 |
|
1023 | You can also configure your own access log format by using the `AccessLogFormat.custom()` API.
|
1024 | `AccessLogField` provides commonly used fields. The following code configures access log to contain.
|
1025 |
|
1026 | ```ts
|
1027 | const logGroup = new logs.LogGroup(this, "ApiGatewayAccessLogs");
|
1028 | new apigateway.RestApi(this, 'books', {
|
1029 | deployOptions: {
|
1030 | accessLogDestination: new apigateway.LogGroupLogDestination(logGroup),
|
1031 | accessLogFormat: apigateway.AccessLogFormat.custom(
|
1032 | `${apigateway.AccessLogField.contextRequestId()} ${apigateway.AccessLogField.contextErrorMessage()} ${apigateway.AccessLogField.contextErrorMessageString()}`
|
1033 | )
|
1034 | }
|
1035 | });
|
1036 | ```
|
1037 |
|
1038 | You can use the `methodOptions` property to configure
|
1039 | [default method throttling](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-throttling.html#apigateway-api-level-throttling-in-usage-plan)
|
1040 | for a stage. The following snippet configures the a stage that accepts
|
1041 | 100 requests per minute, allowing burst up to 200 requests per minute.
|
1042 |
|
1043 | ```ts
|
1044 | const api = new apigateway.RestApi(this, 'books');
|
1045 | const deployment = new apigateway.Deployment(this, 'my-deployment', { api });
|
1046 | const stage = new apigateway.Stage(this, 'my-stage', {
|
1047 | deployment,
|
1048 | methodOptions: {
|
1049 | '/*/*': { // This special path applies to all resource paths and all HTTP methods
|
1050 | throttlingRateLimit: 100,
|
1051 | throttlingBurstLimit: 200
|
1052 | }
|
1053 | }
|
1054 | });
|
1055 | ```
|
1056 |
|
1057 | Configuring `methodOptions` on the `deployOptions` of `RestApi` will set the
|
1058 | throttling behaviors on the default stage that is automatically created.
|
1059 |
|
1060 | ```ts
|
1061 | const api = new apigateway.RestApi(this, 'books', {
|
1062 | deployOptions: {
|
1063 | methodOptions: {
|
1064 | '/*/*': { // This special path applies to all resource paths and all HTTP methods
|
1065 | throttlingRateLimit: 100,
|
1066 | throttlingBurstLimit: 1000
|
1067 | }
|
1068 | }
|
1069 | }
|
1070 | });
|
1071 | ```
|
1072 |
|
1073 | ## Cross Origin Resource Sharing (CORS)
|
1074 |
|
1075 | [Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) is a mechanism
|
1076 | that uses additional HTTP headers to tell browsers to give a web application
|
1077 | running at one origin, access to selected resources from a different origin. A
|
1078 | web application executes a cross-origin HTTP request when it requests a resource
|
1079 | that has a different origin (domain, protocol, or port) from its own.
|
1080 |
|
1081 | You can add the CORS [preflight](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Preflighted_requests) OPTIONS
|
1082 | HTTP method to any API resource via the `defaultCorsPreflightOptions` option or by calling the `addCorsPreflight` on a specific resource.
|
1083 |
|
1084 | The following example will enable CORS for all methods and all origins on all resources of the API:
|
1085 |
|
1086 | ```ts
|
1087 | new apigateway.RestApi(this, 'api', {
|
1088 | defaultCorsPreflightOptions: {
|
1089 | allowOrigins: apigateway.Cors.ALL_ORIGINS,
|
1090 | allowMethods: apigateway.Cors.ALL_METHODS // this is also the default
|
1091 | }
|
1092 | })
|
1093 | ```
|
1094 |
|
1095 | The following example will add an OPTIONS method to the `myResource` API resource, which
|
1096 | only allows GET and PUT HTTP requests from the origin <https://amazon.com.>
|
1097 |
|
1098 | ```ts
|
1099 | declare const myResource: apigateway.Resource;
|
1100 |
|
1101 | myResource.addCorsPreflight({
|
1102 | allowOrigins: [ 'https://amazon.com' ],
|
1103 | allowMethods: [ 'GET', 'PUT' ]
|
1104 | });
|
1105 | ```
|
1106 |
|
1107 | See the
|
1108 | [`CorsOptions`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-apigateway.CorsOptions.html)
|
1109 | API reference for a detailed list of supported configuration options.
|
1110 |
|
1111 | You can specify defaults this at the resource level, in which case they will be applied to the entire resource sub-tree:
|
1112 |
|
1113 | ```ts
|
1114 | declare const resource: apigateway.Resource;
|
1115 |
|
1116 | const subtree = resource.addResource('subtree', {
|
1117 | defaultCorsPreflightOptions: {
|
1118 | allowOrigins: [ 'https://amazon.com' ]
|
1119 | }
|
1120 | });
|
1121 | ```
|
1122 |
|
1123 | This means that all resources under `subtree` (inclusive) will have a preflight
|
1124 | OPTIONS added to them.
|
1125 |
|
1126 | See [#906](https://github.com/aws/aws-cdk/issues/906) for a list of CORS
|
1127 | features which are not yet supported.
|
1128 |
|
1129 | ## Endpoint Configuration
|
1130 |
|
1131 | API gateway allows you to specify an
|
1132 | [API Endpoint Type](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-api-endpoint-types.html).
|
1133 | To define an endpoint type for the API gateway, use `endpointConfiguration` property:
|
1134 |
|
1135 | ```ts
|
1136 | const api = new apigateway.RestApi(this, 'api', {
|
1137 | endpointConfiguration: {
|
1138 | types: [ apigateway.EndpointType.EDGE ]
|
1139 | }
|
1140 | });
|
1141 | ```
|
1142 |
|
1143 | You can also create an association between your Rest API and a VPC endpoint. By doing so,
|
1144 | API Gateway will generate a new
|
1145 | Route53 Alias DNS record which you can use to invoke your private APIs. More info can be found
|
1146 | [here](https://docs.aws.amazon.com/apigateway/latest/developerguide/associate-private-api-with-vpc-endpoint.html).
|
1147 |
|
1148 | Here is an example:
|
1149 |
|
1150 | ```ts
|
1151 | declare const someEndpoint: ec2.IVpcEndpoint;
|
1152 |
|
1153 | const api = new apigateway.RestApi(this, 'api', {
|
1154 | endpointConfiguration: {
|
1155 | types: [ apigateway.EndpointType.PRIVATE ],
|
1156 | vpcEndpoints: [ someEndpoint ]
|
1157 | }
|
1158 | });
|
1159 | ```
|
1160 |
|
1161 | By performing this association, we can invoke the API gateway using the following format:
|
1162 |
|
1163 | ```plaintext
|
1164 | https://{rest-api-id}-{vpce-id}.execute-api.{region}.amazonaws.com/{stage}
|
1165 | ```
|
1166 |
|
1167 | ## Private Integrations
|
1168 |
|
1169 | A private integration makes it simple to expose HTTP/HTTPS resources behind an
|
1170 | Amazon VPC for access by clients outside of the VPC. The private integration uses
|
1171 | an API Gateway resource of `VpcLink` to encapsulate connections between API
|
1172 | Gateway and targeted VPC resources.
|
1173 | The `VpcLink` is then attached to the `Integration` of a specific API Gateway
|
1174 | Method. The following code sets up a private integration with a network load
|
1175 | balancer -
|
1176 |
|
1177 | ```ts
|
1178 | import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2';
|
1179 |
|
1180 | const vpc = new ec2.Vpc(this, 'VPC');
|
1181 | const nlb = new elbv2.NetworkLoadBalancer(this, 'NLB', {
|
1182 | vpc,
|
1183 | });
|
1184 | const link = new apigateway.VpcLink(this, 'link', {
|
1185 | targets: [nlb],
|
1186 | });
|
1187 |
|
1188 | const integration = new apigateway.Integration({
|
1189 | type: apigateway.IntegrationType.HTTP_PROXY,
|
1190 | options: {
|
1191 | connectionType: apigateway.ConnectionType.VPC_LINK,
|
1192 | vpcLink: link,
|
1193 | },
|
1194 | });
|
1195 | ```
|
1196 |
|
1197 | The uri for the private integration, in the case of a VpcLink, will be set to the DNS name of
|
1198 | the VPC Link's NLB. If the VPC Link has multiple NLBs or the VPC Link is imported or the DNS
|
1199 | name cannot be determined for any other reason, the user is expected to specify the `uri`
|
1200 | property.
|
1201 |
|
1202 | Any existing `VpcLink` resource can be imported into the CDK app via the `VpcLink.fromVpcLinkId()`.
|
1203 |
|
1204 | ```ts
|
1205 | const awesomeLink = apigateway.VpcLink.fromVpcLinkId(this, 'awesome-vpc-link', 'us-east-1_oiuR12Abd');
|
1206 | ```
|
1207 |
|
1208 | ## Gateway response
|
1209 |
|
1210 | If the Rest API fails to process an incoming request, it returns to the client an error response without forwarding the
|
1211 | request to the integration backend. API Gateway has a set of standard response messages that are sent to the client for
|
1212 | each type of error. These error responses can be configured on the Rest API. The list of Gateway responses that can be
|
1213 | configured can be found [here](https://docs.aws.amazon.com/apigateway/latest/developerguide/supported-gateway-response-types.html).
|
1214 | Learn more about [Gateway
|
1215 | Responses](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-gatewayResponse-definition.html).
|
1216 |
|
1217 | The following code configures a Gateway Response when the response is 'access denied':
|
1218 |
|
1219 | ```ts
|
1220 | const api = new apigateway.RestApi(this, 'books-api');
|
1221 | api.addGatewayResponse('test-response', {
|
1222 | type: apigateway.ResponseType.ACCESS_DENIED,
|
1223 | statusCode: '500',
|
1224 | responseHeaders: {
|
1225 | 'Access-Control-Allow-Origin': "test.com",
|
1226 | 'test-key': 'test-value'
|
1227 | },
|
1228 | templates: {
|
1229 | 'application/json': '{ "message": $context.error.messageString, "statusCode": "488", "type": "$context.error.responseType" }'
|
1230 | }
|
1231 | });
|
1232 | ```
|
1233 |
|
1234 | ## OpenAPI Definition
|
1235 |
|
1236 | CDK supports creating a REST API by importing an OpenAPI definition file. It currently supports OpenAPI v2.0 and OpenAPI
|
1237 | v3.0 definition files. Read more about [Configuring a REST API using
|
1238 | OpenAPI](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-import-api.html).
|
1239 |
|
1240 | The following code creates a REST API using an external OpenAPI definition JSON file -
|
1241 |
|
1242 | ```ts
|
1243 | declare const integration: apigateway.Integration;
|
1244 |
|
1245 | const api = new apigateway.SpecRestApi(this, 'books-api', {
|
1246 | apiDefinition: apigateway.ApiDefinition.fromAsset('path-to-file.json')
|
1247 | });
|
1248 |
|
1249 | const booksResource = api.root.addResource('books')
|
1250 | booksResource.addMethod('GET', integration);
|
1251 | ```
|
1252 |
|
1253 | It is possible to use the `addResource()` API to define additional API Gateway Resources.
|
1254 |
|
1255 | **Note:** Deployment will fail if a Resource of the same name is already defined in the Open API specification.
|
1256 |
|
1257 | **Note:** Any default properties configured, such as `defaultIntegration`, `defaultMethodOptions`, etc. will only be
|
1258 | applied to Resources and Methods defined in the CDK, and not the ones defined in the spec. Use the [API Gateway
|
1259 | extensions to OpenAPI](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-swagger-extensions.html)
|
1260 | to configure these.
|
1261 |
|
1262 | There are a number of limitations in using OpenAPI definitions in API Gateway. Read the [Amazon API Gateway important
|
1263 | notes for REST APIs](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-known-issues.html#api-gateway-known-issues-rest-apis)
|
1264 | for more details.
|
1265 |
|
1266 | **Note:** When starting off with an OpenAPI definition using `SpecRestApi`, it is not possible to configure some
|
1267 | properties that can be configured directly in the OpenAPI specification file. This is to prevent people duplication
|
1268 | of these properties and potential confusion.
|
1269 |
|
1270 | ### Endpoint configuration
|
1271 |
|
1272 | By default, `SpecRestApi` will create an edge optimized endpoint.
|
1273 |
|
1274 | This can be modified as shown below:
|
1275 |
|
1276 | ```ts
|
1277 | declare const apiDefinition: apigateway.ApiDefinition;
|
1278 |
|
1279 | const api = new apigateway.SpecRestApi(this, 'ExampleRestApi', {
|
1280 | apiDefinition,
|
1281 | endpointTypes: [apigateway.EndpointType.PRIVATE]
|
1282 | });
|
1283 | ```
|
1284 |
|
1285 | **Note:** For private endpoints you will still need to provide the
|
1286 | [`x-amazon-apigateway-policy`](https://docs.aws.amazon.com/apigateway/latest/developerguide/openapi-extensions-policy.html) and
|
1287 | [`x-amazon-apigateway-endpoint-configuration`](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-swagger-extensions-endpoint-configuration.html)
|
1288 | in your openApi file.
|
1289 |
|
1290 | ## Metrics
|
1291 |
|
1292 | The API Gateway service sends metrics around the performance of Rest APIs to Amazon CloudWatch.
|
1293 | These metrics can be referred to using the metric APIs available on the `RestApi` construct.
|
1294 | The APIs with the `metric` prefix can be used to get reference to specific metrics for this API. For example,
|
1295 | the method below refers to the client side errors metric for this API.
|
1296 |
|
1297 | ```ts
|
1298 | const api = new apigateway.RestApi(this, 'my-api');
|
1299 | const clientErrorMetric = api.metricClientError();
|
1300 | ```
|
1301 |
|
1302 | ## APIGateway v2
|
1303 |
|
1304 | APIGateway v2 APIs are now moved to its own package named `aws-apigatewayv2`. For backwards compatibility, existing
|
1305 | APIGateway v2 "CFN resources" (such as `CfnApi`) that were previously exported as part of this package, are still
|
1306 | exported from here and have been marked deprecated. However, updates to these CloudFormation resources, such as new
|
1307 | properties and new resource types will not be available.
|
1308 |
|
1309 | Move to using `aws-apigatewayv2` to get the latest APIs and updates.
|
1310 |
|
1311 | ----
|
1312 |
|
1313 | This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project.
|