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