One of the major improvements is streamlining API Gateway definition resources. Since single API definition contains plenty of resource, many things need to be repeated. Macro handles dedicated structure that keeps entre API definition in single structure and extracts it into separate resources also making some tedious tasks automated or at least simplified.
Since REST API definition is combining many other resources it is separated into new template section RestApis
- it is a hash with API logical identifier (key in your template) as a key and API properties as each entry:
RestApis: MyApi: Authorizers: # authorizers definitions (see below) Models: # models definitions (see below) Resources: # API structure definition (see below) # all other properties will be mapped directly to API resources, so you can do: Name: "my-public-api"
Each REST API also defines AWS::ApiGateway::Deployment
resource.
Defining any API using RestApis
block also creates AWS::ApiGateway::Account
and logging role for it.
Note: On each level any property not mentioned directly here is passed to underlying resource, so in case of any changes in native CloudFormation resources can be directly used in the macro.
Authorizers: SomeAuthorizerId: Type: "TOKEN" # this is not `!Sub`, this is an API gateway stage variable reference Lambda: "${stageVariables.AuthorizerLambda}" IdentitySource: "method.request.header.Authorization" AuthorizerResultTtlInSeconds: 3600
This is a completely ordinary authorizer definition except that it is automatically scoped within API. You also don’t need to define its name.
Note: For Lambda
property see “Simplifications” section.
Models: AccountInfo: Schema: type: "object" properties: accountName: type: string required: - "accountName" additionalProperties: false
Again, definition of model is just a native AWS::ApiGateway::Model
just automatically scoped to current API. Content type, schema type and model title are, however, optional. You can see “Simplifications” section for details.
The biggest simplification comes from converting excessive definitions of resource resources (well… resources of resources?) into structure definition - a little like Open API directly in CloudFormation template:
# keep in mind - this is `Resources` section of API not a top-level template section Resources: /accounts: /{accountId}: "@GET": # integration method - see below "@DELETE": # integration method - see below /keys: "@GET": # integration method - see below
There is literally no resource definition needed, as it is extracted directly from the tree-ish structure. It is just a container for integration methods definitions.
"@PUT": Authorizer: "TokenAuthorizer" RequestValidator: "BODY_AND_PARAMETERS" RequestModels: application/json: !Ref "RestApi:V1:Model:TenantInfo" RequestParameters: method.request.header.Authorization: true method.request.path.clientTenantId: true method.request.path.apiKey: true Integration: Lambda: "${stageVariables.TenantKeySavingTarget}" Credentials: !GetAtt "ApiGatewayRole.Arn" RequestTemplates: application/json: | { "tenantId": "$context.authorizer.tenantId", "clientTenantId": "$input.params('clientTenantId')", "apiKey": "$input.params('apiKey')", "tenantInfo": $input.json('$') } IntegrationResponses: "202": ResponseParameters: method.response.header.Content-Type: "'application/atom+xml'" "404": SelectionPattern: ".*Tag not found.*" PassthroughBehavior: "NEVER"
AWS::ApiGateway::Method
definition with some changes.@PUT
its set to PUT
.Authorizer
(instead of AuthorizerId
) is just an identifier from Authorizers
definitions - macro converts it to proper reference. AuthorizerType
is also automatically assigned based on referred authorizer type.RequestValidator
(instead of RequestValidatorId
) is just one of the three values: BODY_ONLY
, PARAMETERS_ONLY, or
BODY_AND_PARAMETERS`. Underlying validators are managed on-demand by macro.MethodResponses
and Integration.IntegrationResponses
can be defined as maps, where keys become StatusCode
of defined responses (you can still use native notation of CloudFormation template).MethodResponses
is defined directly in the method it is built based on Integration.IntegrationResponses
by leaving StatusCode
and ResponseParameters
(if present) replaced by boolean flags instead of any values.Note: Mapping keys in CloudFormation templates must be strings, even if it’s 202
it must be wrapped within quotes!
You can refer to any resource defined by an API using specific notation (macro replaces them in Ref
, Fn::Sub
and Fn::GetAtt
):
RestApi:MyApi
- returns API reference;RestApi:MyApi:Deployment
- current API reference deployment;RestApi:MyApi:Authorizer:AuthorizerIdentifier
- authorizer reference;RestApi:MyApi:Validator:ValidatorIdentifier
- validator reference (it can be BODY_ONLY
, PARAMETERS_ONLY
or BODY_AND_PARAMETERS
);RestApi:MyApi:Model:ModelIdentifier
- model reference;RestApi:MyApi:Resource:ResourcePath
- resource reference;RestApi:MyApi:Method:MethodAndResourcePath
- integration method reference.In case of Resource
and Method
references you need to use %param%
instead of {param}
path placeholder as that would collide with Fn::Sub
calls.
Examples of references:
!Ref "RestApi:MyApi:Authorizer:TokenAuthorizer"
!GetAtt "RestApi:MyApi.RootResourceId"
!Sub "${RestApi:MyApi:Resource:/%accountId%}"
Type
defaults to AWS
and IntegrationHttpMethod
defaults to POST
.Lambda
property instead of Uri
and AuthorizerUri
respectively - for that you can define just a Lambda function name (can be a reference or even stage variable).application/json
, it’s $schema
to http://json-schema.org/draft-04/schema#
and title
to its template identifier.Name
of validator is not required - it defaults to its template identifier.RestApis: # ApiGatewayV1 V1: Name: "MyTestApi" Authorizers: TokenAuthorizer: Type: "TOKEN" Lambda: "${stageVariables.AuthorizerLambda}" IdentitySource: "method.request.header.Authorization" AuthorizerResultTtlInSeconds: 3600 Models: AccountInfo: Schema: type: "object" properties: accountName: type: string required: - "accountName" additionalProperties: false Resources: /accounts: /{accountId}: "@DELETE": Authorizer: "TokenAuthorizer" RequestValidator: "PARAMETERS_ONLY" RequestParameters: method.request.header.Authorization: true method.request.path.accountId: true Integration: Lambda: "${stageVariables.AccountDeletionTarget}" Credentials: !GetAtt "ApiGatewayRole.Arn" RequestTemplates: application/json: | { "tenantId": "$context.authorizer.tenantId", "accountId": "$input.params('accountId')" } IntegrationResponses: "202": {} PassthroughBehavior: "NEVER" /keys: /{apiKey}: "@PUT": Authorizer: "TokenAuthorizer" RequestValidator: "BODY_AND_PARAMETERS" RequestModels: application/json: !Ref "RestApi:V1:Model:AccountInfo" RequestParameters: method.request.header.Authorization: true method.request.path.accountId: true method.request.path.apiKey: true Integration: Lambda: "${stageVariables.AccountKeySavingTarget}" Credentials: !GetAtt "ApiGatewayRole.Arn" RequestTemplates: application/json: | { "tenantId": "$context.authorizer.tenantId", "accountId": "$input.params('accountId')", "apiKey": "$input.params('apiKey')", "accountInfo": $input.json('$') } IntegrationResponses: "202": {} PassthroughBehavior: "NEVER" Outputs: RestApiId: Description: "API Gateway ID." Value: !Ref "RestApi:V1"