こんにちは、CX事業本部 IoT事業部の若槻です。
Amazon API GatewayのAWS Integrationでは、Lambda以外にも様々なAWS ServiceをAPIに統合して、公開することが可能です。
今回は、Amazon API GatewayとStep Functions Express Workflowを統合して同期実行可能なREST APIを作ってみました。
やってみた
下記のような構成を作成します。
実装
AWS CDK v2(TypeScript)で次のCDKスタックを作成します。
lib/cdk-app-stack.ts
import { Construct } from 'constructs';
import {
aws_apigateway,
aws_stepfunctions,
Stack,
StackProps,
} from 'aws-cdk-lib';
export class CdkAppStack extends Stack {
constructor(scope: Construct, id: string, props: StackProps) {
super(scope, id, props);
const stateMachine = new aws_stepfunctions.StateMachine(
this,
'stateMachine',
{
stateMachineName: 'expressWorkflow',
stateMachineType: aws_stepfunctions.StateMachineType.EXPRESS,
definition: new aws_stepfunctions.Pass(this, 'pass', {
parameters: {
key1: 'val1',
key2: aws_stepfunctions.JsonPath.stringAt('$'),
},
}),
},
);
const restApi = new aws_apigateway.RestApi(this, 'restApi');
restApi.root.addMethod(
'GET',
aws_apigateway.StepFunctionsIntegration.startExecution(stateMachine),
);
restApi.root
.addResource('{id}')
.addMethod(
'POST',
aws_apigateway.StepFunctionsIntegration.startExecution(stateMachine),
);
}
}
aws-cdk-lib » aws_apigateway
に用意されているStepFunctionsIntegrationというClassを使えば、REST APIのMethodにStepFunctionsのIntegrationを簡単に設定することができます。- class StepFunctionsIntegration · AWS CDK
EXPRESS
TypeのState Machineを使うことによりWorkflowのOutputをAPI Clientから同期的に取得できるようにしています。
- WorkflowのOutputは、
key1
は固定値val1
、key2
はInputを指定して返すようにしています。
上記をCDK Deployしてスタックをデプロイします。
動作確認
API GatewayのコンソールからREST APIにリクエストをしてみます。
まず/
に対してGETリクエストを行うと、Response BodyでWorkflow実行のOutputを同期的に取得できていますね。
次に/{id}
に対してPOSTリクエストを行います。Pathパラメータ、QueryStringパラメータおよびBodyがWorkflowのInputとして使用できていますね。
StepFunctionsIntegration Classにより設定されるMapping Template
今回AWS CDKのStepFunctionsIntegration Classを使用しましたが、これによりMethodにMapping Templateが設定され、REST APIとStep Functionsの間のリクエスト/レスポンス時の入出力の変換を適切に行なってくれるようになります。
Integration Requestでは、次のようなMapping Templateが作成されます。リクエスト時のbody、header、pathそしてquerystringがStep Functionsにシンプルな形式で渡され、WorkflowのInputとして使えるようになります。
Integration Request
## Velocity Template used for API Gateway request mapping template
##
## This template forwards the request body, header, path, and querystring
## to the execution input of the state machine.
##
## "@@" is used here as a placeholder for '"' to avoid using escape characters.
#set($inputString = '')
#set($includeHeaders = false)
#set($includeQueryString = true)
#set($includePath = true)
#set($includeAuthorizer = false)
#set($allParams = $input.params())
{
"stateMachineArn": "arn:aws:states:ap-northeast-1:XXXXXXXXXXXX:stateMachine:expressWorkflow",
#set($inputString = "$inputString,@@body@@: $input.body")
#if ($includeHeaders)
#set($inputString = "$inputString, @@header@@:{")
#foreach($paramName in $allParams.header.keySet())
#set($inputString = "$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.header.get($paramName))@@")
#if($foreach.hasNext)
#set($inputString = "$inputString,")
#end
#end
#set($inputString = "$inputString }")
#end
#if ($includeQueryString)
#set($inputString = "$inputString, @@querystring@@:{")
#foreach($paramName in $allParams.querystring.keySet())
#set($inputString = "$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.querystring.get($paramName))@@")
#if($foreach.hasNext)
#set($inputString = "$inputString,")
#end
#end
#set($inputString = "$inputString }")
#end
#if ($includePath)
#set($inputString = "$inputString, @@path@@:{")
#foreach($paramName in $allParams.path.keySet())
#set($inputString = "$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.path.get($paramName))@@")
#if($foreach.hasNext)
#set($inputString = "$inputString,")
#end
#end
#set($inputString = "$inputString }")
#end
#if ($includeAuthorizer)
#set($inputString = "$inputString, @@authorizer@@:{")
#foreach($paramName in $context.authorizer.keySet())
#set($inputString = "$inputString @@$paramName@@: @@$util.escapeJavaScript($context.authorizer.get($paramName))@@")
#if($foreach.hasNext)
#set($inputString = "$inputString,")
#end
#end
#set($inputString = "$inputString }")
#end
#set($requestContext = "")
## Check if the request context should be included as part of the execution input
#if($requestContext && !$requestContext.empty)
#set($inputString = "$inputString,")
#set($inputString = "$inputString @@requestContext@@: $requestContext")
#end
#set($inputString = "$inputString}")
#set($inputString = $inputString.replaceAll("@@",'"'))
#set($len = $inputString.length() - 1)
"input": "{$util.escapeJavaScript($inputString.substring(1,$len))}"
}
Integration Responseでも、次のようなMapping Templateが作成されます。これによりoutput
のみがResponse Bodyとして返るようになります。
Integration Response
#set($inputRoot = $input.path('$'))
#if($input.path('$.status').toString().equals("FAILED"))
#set($context.responseOverride.status = 500)
{
"error": "$input.path('$.error')",
"cause": "$input.path('$.cause')"
}
#else
$input.path('$.output')
#end
StepFunctionsIntegration Class、めちゃくちゃ優秀ですね!
おわりに
Amazon API Gateway + Step Functions Express WorkflowでREST APIを作ってみました。
Step Functionsは9000以上のAWS API Actionを直接実行できるので、今回の実装を活用すればLambda要らずがさらに加速しますね!
参考
- API Gateway REST API + Cognito User Pool Authorizer + Lambda Functionな構成をAWS CDK v2で構築してみた | DevelopersIO
- API Gateway を使用してStep FunctionsのAPIを作成してみた。 | DevelopersIO
- AWS Step Functions Express WorkflowをAWS CDKで作ってみた | DevelopersIO
以上