Define your API via OpenAPI definition on AWS

Application Programming Interfaces(APIs) is a critical part of the web service, Werner Vogel, the CTO of AWS had a great 6 Rules for Good API Design presentation in 2021 re:Invent keynote.

In AWS the developers could manage and proxy the APIs via Amazon API Gateway. The developers can use console, CLI, API or IaC code(for example, Terraform/CloudFormation/CDK) to provisioning the API resources on AWS. However some developers might flavor with using OpenAPI specification to define the APIs. It enables multiple services/tools to understand the APIs' specification, such as Postman. Amazon API Gateway supports this use case, you can import the existing OpenAPI definition as API.

Amazon API Gateway offers two RESTful API products, REST API and HTTP API. Both of those two APIs support importing OpenAPI definition, but they might use different OpenAPI extensions to support different features.

And below example will use infrastructure as code(AWS CDK) to import the OpenAPI definition to the API Gateway APIs. While importing OpenAPI definition, the most challenge is updating the OpenAPI definition with dynamic resources information(for example, IAM role for calling downstream resources of integration) before importing the OpenAPI definition. For AWS CDK(on top of AWS CloudFormation) uses the intrinsic functions of CloudFormation(Fn::Join) to archive it.

 1    const deployOptions = {
 2      stageName: '',
 3      loggingLevel: MethodLoggingLevel.ERROR,
 4      dataTraceEnabled: false,
 5      metricsEnabled: true,
 6      tracingEnabled: false,
 7    };
 8    const restOpenAPISpec = this.resolve(Mustache.render(
 9      fs.readFileSync(path.join(__dirname, './rest-sqs.yaml'), 'utf-8'),
10      variables));
11    new SpecRestApi(this, 'rest-to-sqs', {
12      apiDefinition: ApiDefinition.fromInline(restOpenAPISpec),
13      endpointExportName: 'APIEndpoint',
14      deployOptions,
15    });

But above solution does not work with HTTP API, because the CloudFormation of HTTP API does not support intrinsic functions of CFN. 😥 The workaround is putting the OpenAPI definition to Amazon S3 firstly, then import it from S3 bucket via CloudFormation. It involves putting the OpenAPI definition with dynamic resource information to S3 bucket before importing the OpenAPI definition from S3. Here I leveage the CDK built-in custom resource to call S3 API to put the OpenAPI definition file to S3.

22/11/09 UPDATE: The Body of AWS::ApiGatewayV2::Api only supports the json object. It works after converting the Yaml OpenAPI definition to JSON!

 1const yaml = require('js-yaml');
 5    // import openapi as http api
 6    const variables = {
 7      integrationRoleArn: apiRole.roleArn,
 8      queueName: bufferQueue.queueName,
 9      queueUrl: bufferQueue.queueUrl,
10    };
11    const openAPISpec = this.resolve(yaml.load(Mustache.render(
12      fs.readFileSync(path.join(__dirname, './http-sqs.yaml'), 'utf-8'), variables)));
14    const httpApi = new CfnApi(this, 'http-api-to-sqs', {
15      body: openAPISpec,
16      failOnWarnings: false,
17    });

The example code creates both REST API and HTTP API, both of them forwards the events to Amazon SQS queue that are sent by HTTP POST requests. See OpenAPI definition of HTTP to SQS, OpenAPI definition of REST to SQS or complete source for further reference.

Posts in this Series