API have configuration requirements based on their usage and prefix.
All API paths for service-to-service communication should be prefixed by /backend. For example: /backend/validate. Authorization between services should be performed using IAM credentials.
To add the authorizer, you can add x-amazon-apigateway-auth in the operation section of the OpenAPI document. For example:
paths:
/backend/validate:
post:
x-amazon-apigateway-auth:
type: AWS_IAMOn the client side, you can use the aws-requests-auth python library to generate a valid signature for the requests module. For example:
from urllib.parse import urlparse
from aws_requests_auth.boto_utils import BotoAWSRequestsAuth
import requests
# Gather the domain name and AWS region
url = urlparse(endpoint_url)
region = boto3.session.Session().region_name
# Create the signature helper
iam_auth = BotoAWSRequestsAuth(aws_host=url.netloc,
aws_region=region,
aws_service='execute-api')
# Send a GET request
response = requests.get(endpoint_url, auth=iam_auth)Admin-only paths should be prefixed by /admin. For example: PUT /admin/{productId}.
All other requests should not use any specific prefix. For example: GET /{productId}.
When creating new events, services should use the following naming convention for detail-types.
This event results from the creation of a specific resource owned by the service. The detail must contain all the values of that resource.
This event results from the deletion of a specific resource owned by the service. The detail must contain the resource identifier (e.g. resourceId) and can contain all the other information.
This event results from the modification of a specific resource owned by the service. The detail must contain three parameters: old, new and changed.
oldshould contain the old values of the resource.newshould contain the new values.changedshould contain an array of parameter names that were changed. For nested parameters, this could be either the root parameter name (rootParam), or the list of parameters separated by dots (rootParam.childParam). If the modification concerns an element in an array, the array itself should be referenced (rootParam.arrayParam), not the specific index (notrootParam.arrayParam[3]).
For example:
{
"source": "ecommerce.products",
"detail-type": "ProductChanged",
"resources": ["c60d29c0-434e-4efc-b893-1c604d0718cc"],
"detail": {
"old": {
"productId": "c60d29c0-434e-4efc-b893-1c604d0718cc",
"name": "Sample Product",
"price": 500,
"package": {
"width": 500,
"height": 300,
"length": 200,
"weight": 1000
}
},
"new": {
"productId": "c60d29c0-434e-4efc-b893-1c604d0718cc",
"price": 500,
"package": {
"width": 500,
"height": 300,
"length": 350,
"weight": 1000
}
},
"changed": [
"price",
"package.length"
]
}
}This event results from the failure to perform an operation that is owned by the service. The detail should contain the resource identifier on which the operation applies.
Passing resources such as Amazon API Gateway URLs, SNS topics, etc. across services must be done through SSM Parameters.
SSM Parameter names should follow this convention: /ecommerce/{environment}/{serviceName}/{resourceName}/{type}. For example:
/ecommerce/dev/users/user-pool/arn
/ecommerce/prod/orders/api/url
Here is how to create SSM parameters for your resources in CloudFormation:
Resources:
UserPool:
Type: AWS::Cognito::UserPool
Properties:
# ...
UserPoolArnParameter:
Type: AWS::SSM::Parameter
Properties:
Name: !Sub /ecommerce/${Environment}/users/user-pool/arn
Type: String
Value: !GetAtt UserPool.ArnTo use resources in your service, you should use CloudFormation parameters. For example:
Parameters:
UserPoolArn:
Type: AWS::SSM::Parameter::Value<String>
Description: Cognito User Pool ARNTo minimize the risk of errors when using multiple environments, you should not set a default value to the parameter. You should instead add the parameter in the metadata.yaml file for automatic transformation:
parameters:
# Note the lack of '$' here
UserPoolArn: /ecommerce/{Environment}/users/user-pool/arnServices should use Python 3.9 whenever possible, both for tests and for Lambda function code. In the same way, internal tools should be made using Python 3.9.
Tests should be written for pytest.
Each service should have the following structure in its folder:
/{service}/Makefile: File containing the build instructions for the service./{service}/metadata.yaml: File containing information about the service, such as its name, permissions, dependencies and parameters./{service}/resources/openapi.yaml(optional): File containing the OpenAPI specification. This is optional if the service does not provide an API./{service}/resources/events.yaml(optional): File containing the event schemas for EventBridge in OpenAPI format. This is optional if the service does not emit events./{service}/src/{function}/(optional): Source code for Lambda functions. This is optional if the service does not provide Lambda functions or include the code in the template itself./{service}/template.yaml: CloudFormation template for the service./{service}/tests/integ/(optional): Contains integration tests that are run on a deployed infrastructure./{service}/tests/unit/{function}/(optional): Contains unit tests that are run locally.