Get Started

smithy-cdk substitutes CDK resources into an OpenAPI model specification file generated by Smithy, so that a REST API Gateway is deployed in one go with proper integrations and permissions for API paths. See the project README for more motivations.

The development cycle works like this:

  1. Define API model in Smithy files. Where an AWS Service is needed for a URI, like the ARN of a lambda function, instead put a ${substitution key}
  2. Build the Smithy model
  3. Create a SmithyApiDefinition with generated JSON model, and supply SmithyIntegrations to inject CDK Tokens

Example

In your Smithy build file configure API Gateway settings. This example shows settings for enabling smithy -> open-api conversion and API Gateway extensions.

smithy-build.json

{
  "version": "1.0",
  "sources": ["models"],
  "maven": {
    "dependencies": [
      "software.amazon.smithy:smithy-openapi:1.41.1",
      "software.amazon.smithy:smithy-aws-traits:1.41.1",
      "software.amazon.smithy:smithy-aws-apigateway-openapi:1.42.0"
    ]
  },
  "projections": {
    "openapi-conversion": {
      "plugins": {
        "openapi": {
          "service": "smithycdk#HiByeApi",
          "protocol": "aws.protocols#restJson1",
          "version": "3.0.2", // <---- current latest OpenAPI version supported by API Gateway
          "apiGatewayDefaults": "2023-08-11",
          "disableCloudFormationSubstitution": false
        }
      }
    }
  }
}

Now in the Smithy API model add API Gateway traits. This adds a lambda integration to the /hi endpoint. The URI field is set to a substitution key SayHiLambda by wrapping it in ${}.

hi-bye-api.smithy

@readonly
@http(method: "GET", uri: "/hi")
@integration(
    type: "aws_proxy",
    uri: "${SayHiLambda}",
    httpMethod: "POST"
)
operation SayHi {
    input: SayHiInput
    output: SayHiOutput
}

After running smithy build the following open-api JSON is generated, this snippet shows the JSON for the API Gateway integration of the /hi endpoint.

HiByeApi.openapi.json

{
    /* ... */
    "x-amazon-apigateway-integration": {
        "type": "aws_proxy",
        "uri": {
            "Fn::Sub": "${SayByeLambda}"
        },
        "httpMethod": "POST"
    }
    /* ... */
}

Then, in CDK code supply a SmithyLambdaIntegration to match the substitution key SayHiLambda. Permissions will be automatically created for the API Gateway endpoint to invoke the Lambda.

smithy-cdk-example-stack.ts

import * as cdk from 'aws-cdk-lib';
import * as node_lambda from 'aws-cdk-lib/aws-lambda-nodejs'
import * as lambda from 'aws-cdk-lib/aws-lambda'

import { SpecRestApi } from 'aws-cdk-lib/aws-apigateway';
import { Construct } from 'constructs';
import { SmithyApiDefinition, SmithyLambdaIntegration } from 'smithy-cdk';

import * as path from 'path'

export class SmithyCdkExampleStack extends cdk.Stack {
    constructor(scope: Construct, id: string, props?: cdk.StackProps) {
        super(scope, id, props);

        // Nodejs Lambda
        const sayHiLambda = new node_lambda.NodejsFunction(this, 'sayhi', {
            description: 'Greets the caller',
            runtime: lambda.Runtime.NODEJS_20_X
        });

        // Loads Smithy model into JSON from file
        const modelJson = buildSmithyModel({
            srcDir: path.join(__dirname, 'smithy'),
            modelName: 'HiByeApi'
        });

        // Create the API Gateway REST API
        new SpecRestApi(this, 'hiByeApi', {
            apiDefinition: new SmithyApiDefinition(modelJson, {
                'SayHiLambda': new SmithyLambdaIntegration(sayHiLambda, { allowTestInvoke: true })
            })
        });
    }
}

After running cdk synth we can see that the lambda integration has been created for the api endpoint, and that permissions have been created in the CloudFormation template

cloudformation output

...
/hi:
  get:
    operationId: SayHi
    parameters:
      - name: name
        in: query
        schema:
          type: string
        required: true
    responses:
      "200":
        description: SayHi 200 response
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SayHiResponseContent"
    x-amazon-apigateway-integration:
      type: aws_proxy
      uri:
        Fn::Join:
          - ""
          - - "arn:"
            - Ref: AWS::Partition
            - ":apigateway:"
            - Ref: AWS::Region
            - :lambda:path/2015-03-31/functions/
            - Fn::GetAtt:
                - sayhi6FD0447D
                - Arn
            - /invocations
      httpMethod: POST
...
aws:cdk:path: SmithyCdkExampleStack/hiByeApi/ApiPermission.Test.SmithyCdkExampleStackhiByeApiDFA9205D.GET..bye
  hiByeApiApiPermissionSmithyCdkExampleStackhiByeApiDFA9205DGEThi1307E9C2:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName:
        Fn::GetAtt:
          - sayhi6FD0447D
          - Arn
      Principal: apigateway.amazonaws.com
      SourceArn:
        Fn::Join:
          - ""
          - - "arn:"
            - Ref: AWS::Partition
            - ":execute-api:"
            - Ref: AWS::Region
            - ":"
            - Ref: AWS::AccountId
            - ":"
            - Ref: hiByeApiF6F479FD
            - /
            - Ref: hiByeApiDeploymentStageprodD116C929
            - /GET/hi

View the full code for this example