AWS DevOps & Developer Productivity Blog
Deploy and manage OpenAPI/Swagger RESTful APIs with the AWS Cloud Development Kit
This post demonstrates how AWS Cloud Development Kit (AWS CDK) Infrastructure as Code (IaC) constructs and AWS serverless technology can be used to build and deploy a RESTful Application Programming Interface (API) defined in the OpenAPI specification. This post uses an example API that describes Widget
resources and demonstrates how to use an AWS CDK Pipeline to:
- Deploy a RESTful API stage to Amazon API Gateway from an OpenAPI specification.
- Build and deploy an AWS Lambda function that contains the API functionality.
- Auto-generate API documentation and publish it to an Amazon Simple Storage Service (Amazon S3)-hosted website served by the Amazon CloudFront content delivery network (CDN) service. This provides technical and non-technical stakeholders with versioned, current, and accessible API documentation.
- Auto-generate client libraries for invoking the API and deploy them to AWS CodeArtifact, which is a fully-managed artifact repository service. This allows API client development teams to integrate with different versions of the API in different environments.
The diagram shown in the following figure depicts the architecture of the AWS services and resources described in this post.
The code that accompanies this post, written in Java, is available here.
Background
APIs must be understood by all stakeholders and parties within an enterprise including business areas, management, enterprise architecture, and other teams wishing to consume the API. Unfortunately, API definitions are often hidden in code and lack up-to-date documentation. Therefore, they remain inaccessible for the majority of the API’s stakeholders. Furthermore, it’s often challenging to determine what version of an API is present in different environments at any one time.
This post describes some solutions to these issues by demonstrating how to continuously deliver up-to-date and accessible API documentation, API client libraries, and API deployments.
AWS CDK
The AWS CDK is a software development framework for defining cloud IaC and is available in multiple languages including TypeScript, JavaScript, Python, Java, C#/.Net, and Go. The AWS CDK Developer Guide provides best practices for using the CDK.
This post uses the CDK to define IaC in Java which is synthesized to a cloud assembly. The cloud assembly includes one to many templates and assets that are deployed via an AWS CodePipeline pipeline. A unit of deployment in the CDK is called a Stack.
OpenAPI specification (formerly Swagger specification)
OpenAPI specifications describe the capabilities of an API and are both human and machine-readable. They consist of definitions of API components which include resources, endpoints, operation parameters, authentication methods, and contact information.
Project composition
The API project that accompanies this post consists of three directories:
app
api
cdk
app directory
This directory contains the code for the Lambda function which is invoked when the Widget
API is invoked via API Gateway. The code has been developed in Java as an Apache Maven project.
The Quarkus framework has been used to define a WidgetResource
class (see src/main/java/aws/sample/blog/cdkopenapi/app/WidgetResources.java
) that contains the methods that align with HTTP Methods of the Widget
API.
api directory
The api directory contains the OpenAPI specification file ( openapi.yaml ). This file is used as the source for:
- Defining the REST API using API Gateway’s support for OpenApi.
- Auto-generating the API documentation.
- Auto-generating the API client artifact.
The api directory also contains the following files:
openapi-generator-config.yaml
: This file contains configuration settings for the OpenAPI Generator framework, which is described in the section CI/CD Pipeline.maven-settings.xml
: This file is used support the deployment of the generated SDKs or libraries (Apache Maven artifacts) for the API and is described in the CI/CD Pipeline section of this post.
This directory contains a sub directory called docker
. The docker
directory contains a Dockerfile which defines the commands for building a Docker image:
FROM ruby:2.6.5-alpine
RUN apk update \
&& apk upgrade --no-cache \
&& apk add --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/v3.14/main/ nodejs=14.20.0-r0 npm \
&& apk add git \
&& apk add --no-cache build-base
# Install Widdershins node packages and ruby gem bundler
RUN npm install -g widdershins \
&& gem install bundler
# working directory
WORKDIR /openapi
# Clone and install the Slate framework
RUN git clone https://github.com/slatedocs/slate
RUN cd slate \
&& bundle install
The Docker image incorporates two open source projects, the NodeJS Widdershins library and the Ruby Slate-framework. These are used together to auto-generate the documentation for the API from the OpenAPI specification. This Dockerfile is referenced and built by the ApiStack
class, which is described in the CDK Stacks section of this post.
cdk directory
This directory contains an Apache Maven Project developed in Java for provisioning the CDK stacks for the Widget
API.
Under the src/main/java
folder, the package aws.sample.blog.cdkopenapi.cdk
contains the files and classes that define the application’s CDK stacks and also the entry point (main method) for invoking the stacks from the CDK Toolkit CLI:
CdkApp.java
: This file contains theCdkApp
class which provides the main method that is invoked from the AWS CDK Toolkit to build and deploy the application stacks.ApiStack.java
: This file contains theApiStack
class which defines the OpenApiBlogAPI stack and is described in the CDK Stacks section of this post.PipelineStack.java
: This file contains thePipelineStack
class which defines the OpenAPIBlogPipeline stack and is described in the CDK Stacks section of this post.ApiStackStage.java
: This file contains theApiStackStage
class which defines a CDK stage. As detailed in the CI/CD Pipeline section of this post, a DEV stage, containing theOpenApiBlogAPI
stack resources for a DEV environment, is deployed from theOpenApiBlogPipeline
pipeline.
CDK stacks
ApiStack
Note that the CDK bundling functionality is used at multiple points in the ApiStack
class to produce CDK Assets. The post, Building, bundling, and deploying applications with the AWS CDK, provides more details regarding using CDK bundling mechanisms.
The ApiStack class defines multiple resources including:
Widget
API Lambda function: This is bundled by the CDK in a Docker container using the Java 11 runtime image.Widget
REST API on API Gateway: The REST API is created from an Inline API Definition which is passed as an S3 CDK Asset. This asset includes a reference to theWidget
API OpenAPI specification located under theapi
folder (seeapi/openapi.yaml
) and builds upon the SpecRestApi construct and API Gateway’s support for OpenApi.- API documentation Docker Image Asset: This is the Docker image that contains the open source frameworks (Widdershins and Slate) that are leveraged to generate the API documentation.
- CDK Asset bundling functionality that leverages the API documentation Docker image to auto-generate documentation for the API.
- An S3 Bucket for holding the API documentation website.
- An origin access identity (OAI) which allows CloudFront to securely serve the S3 Bucket API documentation content.
- A CloudFront distribution which provides CDN functionality for the S3 Bucket website.
Note that the ApiStack
class features the following code which is executed on the Widget
API Lambda construct:
CfnFunction apiCfnFunction = (CfnFunction)apiLambda.getNode().getDefaultChild();
apiCfnFunction.overrideLogicalId("APILambda");
The CDK, by default, auto-assigns an ID for each defined resource but in this case the generated ID is being overridden with “APILambda”. The reason for this is that inside of the Widget
API OpenAPI specification (see api/openapi.yaml
), there is a reference to the Lambda function by name (“APILambda”) so that the function can be integrated as a proxy for each listed API path and method combination. The OpenAPI specification includes this name as a variable to derive the Amazon Resource Name (ARN) for the Lambda function:
uri:
Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${APILambda.Arn}/invocations"
PipelineStack
The PipelineStack
class defines a CDK CodePipline construct which is a higher level construct and pattern. Therefore, the construct doesn’t just map directly to a single CloudFormation resource, but provisions multiple resources to fulfil the requirements of the pattern. The post, CDK Pipelines: Continuous delivery for AWS CDK applications, provides more detail on creating pipelines with the CDK.
final CodePipeline pipeline = CodePipeline.Builder.create(this, "OpenAPIBlogPipeline")
.pipelineName("OpenAPIBlogPipeline")
.selfMutation(true)
.dockerEnabledForSynth(true)
.synth(synthStep)
.build();
CI/CD pipeline
The diagram in the following figure shows the multiple CodePipeline stages and actions created by the CDK CodePipeline construct that is defined in the PipelineStack
class.
The stages defined include the following:
- Source stage: The pipeline is passed the source code contents from this stage.
- Synth stage: This stage consists of a Synth Action that synthesizes the CloudFormation templates for the application’s CDK stacks and compiles and builds the project Lambda API function.
- Update Pipeline stage: This stage checks the
OpenAPIBlogPipeline
stack and reinitiates the pipeline when changes to its definition have been deployed. - Assets stage: The application’s CDK stacks produce multiple file assets (for example, zipped Lambda code) which are published to Amazon S3. Docker image assets are published to a managed CDK framework Amazon Elastic Container Registry (Amazon ECR) repository.
- DEV stage: The API’s CDK stack (
OpenApiBlogAPI
) is deployed to a hypothetical development environment in this stage. A post stage deployment action is also defined in this stage. Through the use of a CDK ShellStep construct, a Bash script is executed that deploys a generated client Java Archive (JAR) for theWidget
API to CodeArtifact. The script employs the OpenAPI Generator project for this purpose:
CodeBuildStep codeArtifactStep = CodeBuildStep.Builder.create("CodeArtifactDeploy")
.input(pipelineSource)
.commands(Arrays.asList(
"echo $REPOSITORY_DOMAIN",
"echo $REPOSITORY_NAME",
"export CODEARTIFACT_TOKEN=`aws codeartifact get-authorization-token --domain $REPOSITORY_DOMAIN --query authorizationToken --output text`",
"export REPOSITORY_ENDPOINT=$(aws codeartifact get-repository-endpoint --domain $REPOSITORY_DOMAIN --repository $REPOSITORY_NAME --format maven | jq .repositoryEndpoint | sed 's/\\\"//g')",
"echo $REPOSITORY_ENDPOINT",
"cd api",
"wget -q https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/5.4.0/openapi-generator-cli-5.4.0.jar -O openapi-generator-cli.jar",
"cp ./maven-settings.xml /root/.m2/settings.xml",
"java -jar openapi-generator-cli.jar batch openapi-generator-config.yaml",
"cd client",
"mvn --no-transfer-progress deploy -DaltDeploymentRepository=openapi--prod::default::$REPOSITORY_ENDPOINT"
))
.rolePolicyStatements(Arrays.asList(codeArtifactStatement, codeArtifactStsStatement))
.env(new HashMap<String, String>() {{
put("REPOSITORY_DOMAIN", codeArtifactDomainName);
put("REPOSITORY_NAME", codeArtifactRepositoryName);
}})
.build();
Running the project
To run this project, you must install the AWS CLI v2, the AWS CDK Toolkit CLI, a Java/JDK 11 runtime, Apache Maven, Docker, and a Git client. Furthermore, the AWS CLI must be configured for a user who has administrator access to an AWS Account. This is required to bootstrap the CDK in your AWS account (if not already completed) and provision the required AWS resources.
To build and run the project, perform the following steps:
- Fork the OpenAPI blog project in GitHub.
- Open the AWS Console and create a connection to GitHub. Note the connection’s ARN.
- In the Console, navigate to AWS CodeArtifact and create a domain and repository. Note the names used.
- From the command line, clone your forked project and change into the project’s directory:
git clone https://github.com/<your-repository-path>
cd <your-repository-path>
- Edit the CDK JSON file at
cdk/cdk.json
and enter the details:
"RepositoryString": "<your-github-repository-path>",
"RepositoryBranch": "<your-github-repository-branch-name>",
"CodestarConnectionArn": "<connection-arn>",
"CodeArtifactDomain": "<code-artifact-domain-name>",
"CodeArtifactRepository": "<code-artifact-repository-name>"
Please note that for setting configuration values in CDK applications, it is recommend to use environment variables or AWS Systems Manager parameters.
- Commit and push your changes back to your GitHub repository:
git push origin main
- Change into the
cdk
directory and bootstrap the CDK in your AWS account if you haven’t already done so (enter “Y” when prompted):
cd cdk
cdk bootstrap
- Deploy the CDK pipeline stack (enter “Y” when prompted):
cdk deploy OpenAPIBlogPipeline
Once the stack deployment completes successfully, the pipeline OpenAPIBlogPipeline
will start running. This will build and deploy the API and its associated resources. If you open the Console and navigate to AWS CodePipeline, then you’ll see a pipeline in progress for the API.
Once the pipeline has completed executing, navigate to AWS CloudFormation to get the output values for the DEV-OpenAPIBlog
stack deployment:
- Select the
DEV-OpenAPIBlog
stack entry and then select the Outputs column. Record the REST_URL value for the key that begins withOpenAPIBlogRestAPIEndpoint
. - Record the CLOUDFRONT_URL value for the key
OpenAPIBlogCloudFrontURL
.
The API ping method at https://<REST_URL>/ping can now be invoked using your browser or an API development tool like Postman. Other API other methods, as defined by the OpenApi specification, are also available for invocation (For example, GET https://<REST_URL>/widgets).
To view the generated API documentation, open a browser at https://< CLOUDFRONT_URL>.
The following figure shows the API documentation website that has been auto-generated from the API’s OpenAPI specification. The documentation includes code snippets for using the API from multiple programming languages.
To view the generated API client code artifact, open the Console and navigate to AWS CodeArtifact. The following figure shows the generated API client artifact that has been published to CodeArtifact.
Cleaning up
- From the command change to the
cdk
directory and remove the API stack in the DEV stage (enter “Y” when prompted):
cd cdk
cdk destroy OpenAPIBlogPipeline/DEV/OpenAPIBlogAPI
- Once this has completed, delete the pipeline stack:
cdk destroy OpenAPIBlogPipeline
- Delete the S3 bucket created to support pipeline operations. Open the Console and navigate to Amazon S3. Delete buckets with the prefix
openapiblogpipeline
.
Conclusion
This post demonstrates the use of the AWS CDK to deploy a RESTful API defined by the OpenAPI/Swagger specification. Furthermore, this post describes how to use the AWS CDK to auto-generate API documentation, publish this documentation to a web site hosted on Amazon S3, auto-generate API client libraries or SDKs, and publish these artifacts to an Apache Maven repository hosted on CodeArtifact.
The solution described in this post can be improved by:
- Building and pushing the API documentation Docker image to Amazon ECR, and then using this image in CodePipeline API pipelines.
- Creating stages for different environments such as TEST, PREPROD, and PROD.
- Adding integration testing actions to make sure that the API Deployment is working correctly.
- Adding Manual approval actions for that are executed before deploying the API to PROD.
- Using CodeBuild caching of artifacts including Docker images and libraries.
About the author: