これは、なにをしたくて書いたもの?
AWS Lambda関数(Node.js)向けの、middyというライブラリの存在を知ったので、ちょっと試してみることにしました。
middy
ライブラリと書きましたが、middyは「AWS Lambda関数を簡単に書くためのミドルウェアエンジンだ」という
説明をしています。
Middy is a very simple middleware engine that allows you to simplify your AWS Lambda code when using Node.js.
middyのWebサイトおよびリポジトリはこちら。
middy | 🛵 The stylish Node.js middleware engine for AWS Lambda
GitHub - middyjs/middy: 🛵 The stylish Node.js middleware engine for AWS Lambda
middyが作られた動機は、こちらに書かれています。
AWS Lambda関数でのコードを書く際には、入力の解析、バリデーション、エラーハンドリングなどビジネスロジック以外にも
対処する必要がある技術的課題があります。
Anyway, when you are writing a handler, you still have to deal with some common technical concerns outside business logic, like input parsing and validation, output serialization, error handling, etc.
これは必要なものですが、ビジネスロジックを汚染してしまい、コードの読みやすさや保守性を難しくしてしまいます。
(Node.jsにおける)一般的なWebフレームワーク、たとえばfastify、hapi、expressなどはこれをミドルウェアパターンを
用いて解決しています。
ですが、AWS Lambda関数環境下で同様のアプローチを実現しているものがなかったので、middyを作ったようです。
middyの雰囲気を理解するためのコード例は、こちら。
middyとミドルウェア
middyはハンドラーとなる関数をmiddy
関数でラップすることで、ミドルウェアの追加を可能にします。
ミドルウェアの実行フェーズにはbeforeとafterがあり、それぞれハンドラー関数の前後で実行されます。
ドキュメントからの抜粋ですが、3つのミドルウェアを追加した場合、実行順は以下になります。
- middleware1 (before)
- middleware2 (before)
- middleware3 (before)
- handler
- middleware3 (after)
- middleware2 (after)
- middleware1 (after)
afterは逆順ですね。
また、ミドルウェアは関数の実行を中止してすぐにレスポンスを返したり、エラーハンドリングも行うことが
できます。
ミドルウェアの書き方については、こちら。
middyには実装済みのミドルウェアがいくつかあり、middyのリポジトリに含まれれるものは現時点で以下になります。
- middy / Misc
- error-logger: Logs errors
- input-output-logger: Logs request and response
- do-not-wait-for-empty-event-loop: Sets callbackWaitsForEmptyEventLoop property to false
- cloudwatch-metrics: Hydrates lambda’s context.metrics property with an instance of AWS MetricLogger
- warmup: Used to pre-warm a lambda function
- middy / Request Transformation
- http-content-negotiation: Parses Accept-* headers and provides utilities for content negotiation (charset, encoding, language and media type) for HTTP requests
- http-header-normalizer: Normalizes HTTP header names to their canonical format
- http-json-body-parser: Automatically parses HTTP requests with JSON body and converts the body into an object. Also handles gracefully broken JSON if used in combination of httpErrorHandler.
- http-multipart-body-parser: Automatically parses HTTP requests with content type multipart/form-data and converts the body into an object.
- http-urlencode-body-parser: Automatically parses HTTP requests with URL encoded body (typically the result of a form submit).
- http-urlencode-path-parser: Automatically parses HTTP requests with URL encoded path.
- s3-key-normalizer: Normalizes key names in s3 events.
- sqs-json-body-parser: Parse body from SQS events
- validator: Automatically validates incoming events and outgoing responses against custom schemas
- middy / Response Transformation
- http-cors: Sets HTTP CORS headers on response
- http-error-handler: Creates a proper HTTP response for errors that are created with the http-errors module and represents proper HTTP errors.
- http-event-normalizer: Normalizes HTTP events by adding an empty object for queryStringParameters, multiValueQueryStringParameters or pathParameters if they are missing.
- http-security-headers: Applies best practice security headers to responses. It’s a simplified port of HelmetJS.
- http-partial-response: Filter response objects attributes based on query string parameters.
- http-response-serializer: HTTP response serializer.
- sqs-partial-batch-failure: handles partially failed SQS batches.
- middy / Fetch Data
- rds-signer: Fetches token for connecting to RDS with IAM users.
- s3-object-response: Gets and write S3 object response.
- secrets-manager: Fetches parameters from AWS Secrets Manager.
- ssm: Fetches parameters from AWS Systems Manager Parameter Store.
- sts: Fetches credentials to assumes IAM roles for connection to other AWS services.
リクエスト、レスポンスに関するものから、AWSのサービスを利用するものまでありますね。
この他、コミュニティによるミドルウェアもあります。
middy / Community generated middleware
説明はこれくらいにして、とりあえず使ってみましょう。
環境
今回の環境はこちら。LocakStackを使います。
$ localstack --version 0.13.2.1 $ python3 -V Python 3.8.10 $ awslocal --version aws-cli/2.4.8 Python/3.8.8 Linux/5.4.0-91-generic exe/x86_64.ubuntu.20 prompt/off $ samlocal --version SAM CLI, version 1.36.0
LocalStackの起動。
$ LAMBDA_EXECUTOR=docker-reuse localstack start
アプリケーションは、Node.js+TypeScriptで書くことにします。
$ node --version v14.18.2 $ npm --version 6.14.15
middyでHello World
最初に、middyを使ってHello World的なAWS Lambda関数を作成しましょう。この後で、AWS Lambda関数を追加することに
します。
AWS SAMでプロジェクト作成。
$ samlocal init
Node.js 14.xを選びます。
Which template source would you like to use? 1 - AWS Quick Start Templates 2 - Custom Template Location Choice: 1 What package type would you like to use? 1 - Zip (artifact is a zip uploaded to S3) 2 - Image (artifact is an image uploaded to an ECR image repository) Package type: 1 Which runtime would you like to use? 1 - nodejs14.x 2 - python3.9 3 - ruby2.7 4 - go1.x 5 - java11 6 - dotnetcore3.1 7 - nodejs12.x 8 - nodejs10.x 9 - python3.8 10 - python3.7 11 - python3.6 12 - python2.7 13 - ruby2.5 14 - java8.al2 15 - java8 16 - dotnetcore2.1 Runtime: 1 Project name [sam-app]: middy-getting-started Cloning from https://github.com/aws/aws-sam-cli-app-templates AWS quick start application templates: 1 - Hello World Example 2 - Step Functions Sample App (Stock Trader) 3 - Quick Start: From Scratch 4 - Quick Start: Scheduled Events 5 - Quick Start: S3 6 - Quick Start: SNS 7 - Quick Start: SQS 8 - Quick Start: Web Backend Template selection: 1 ----------------------- Generating application: ----------------------- Name: middy-getting-started Runtime: nodejs14.x Architectures: x86_64 Dependency Manager: npm Application Template: hello-world Output Directory: . Next application steps can be found in the README file at ./middy-getting-started/README.md Commands you can use next ========================= [*] Create pipeline: cd middy-getting-started && sam pipeline init --bootstrap [*] Test Function in the Cloud: sam sync --stack-name {stack-name} --watch
プロジェクト内に移動。
$ cd middy-getting-started
いきなりですが、生成されたテンプレートは要らないので削除します。
$ rm -rf hello-world
別のディレクトリを作って移動。
$ mkdir hello-middy $ cd hello-middy
この中で、Node.jsプロジェクトとして初期化します。
$ npm init -y $ npm i -D typescript $ npm i -D -E prettier $ mkdir src
middyを使うには、最低限@middy/core
をインストールすればよいみたいです。TypeScriptで使う場合には、
@types/aws-lambda
も追加します。
$ npm i @middy/core $ npm i -D @types/node@v14 @types/aws-lambda
今回の依存関係は、このようになりました。
"devDependencies": { "@types/aws-lambda": "^8.10.89", "@types/node": "^14.18.5", "prettier": "2.5.1", "typescript": "^4.5.4" }, "dependencies": { "@middy/core": "^2.5.4" }
設定ファイル。
tsconfig.json
{ "compilerOptions": { "target": "esnext", "module": "commonjs", "baseUrl": "./src", "outDir": "dist", "strict": true, "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true, "noImplicitOverride": true, "noImplicitReturns": true, "noPropertyAccessFromIndexSignature": true, "esModuleInterop": true }, "include": [ "src" ] }
.prettierrc.json
{ "singleQuote": true }
package.json
のscripts
定義は、このようにしました。
"scripts": { "build": "tsc --project .", "format": "prettier --write src" },
TypeScriptを使ったコード例は、middyにもサンプルがあるのでこちらを見つつ
こんな感じで作成。
src/app.ts
import middy from '@middy/core'; import { APIGatewayProxyEvent, APIGatewayProxyResult, Context, } from 'aws-lambda'; async function baseHandler( event: APIGatewayProxyEvent ): Promise<APIGatewayProxyResult> { let message; if (event.queryStringParameters && event.queryStringParameters['message']) { message = event.queryStringParameters['message']; } else { message = 'World'; } return { statusCode: 200, body: JSON.stringify({ message: `Hello ${message}!!`, }), }; } export const lambdaHandler = middy(baseHandler);
QueryStringでパラメーターを取れるようにしていますが、単純なAWS Lambda関数です。
ポイントは、ハンドラー関数をmiddy
関数でラップしてexport
していることです。
export const lambdaHandler = middy(baseHandler);
次に、AWS SAMのテンプレートを修正します。生成された時点でこうでしたが、
Resources: HelloWorldFunction: Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction Properties: CodeUri: hello-world/ Handler: app.lambdaHandler Runtime: nodejs14.x Architectures: - x86_64 Events: HelloWorld: Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api Properties: Path: /hello Method: get Outputs: # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function # Find out more about other implicit resources you can reference within SAM # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api HelloWorldApi: Description: "API Gateway endpoint URL for Prod stage for Hello World function" Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/" HelloWorldFunction: Description: "Hello World Lambda Function ARN" Value: !GetAtt HelloWorldFunction.Arn HelloWorldFunctionIamRole: Description: "Implicit IAM Role created for Hello World function" Value: !GetAtt HelloWorldFunctionRole.Arn
これを以下のように修正。
Resources: HelloMiddyFunction: Type: AWS::Serverless::Function Properties: CodeUri: hello-middy/ Handler: app.lambdaHandler Runtime: nodejs14.x Architectures: - x86_64 Events: HelloMiddy: Type: Api Properties: Path: /hello Method: get Metadata: BuildMethod: makefile Outputs: HelloMiddyApi: Description: "API Gateway endpoint URL for Prod stage for Hello Middy function" Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
あとで他のAWS Lambda関数も追加するので、sam build
で一発でビルドできるように最初からMakefile
を用意しました。
Makefile
build-HelloMiddyFunction: npm ci npm run build cp package*.json dist cd dist && \ npm ci --production && \ cp -R * ${ARTIFACTS_DIR}
ひとつ上のディレクトリ(AWS SAMのテンプレートがあるディレクトリ)に戻って
$ cd ..
ビルド。
$ samlocal build
$ awslocal s3 mb s3://my-bucket
デプロイ。
$ samlocal deploy --stack-name my-stack --region us-east-1 --s3-bucket my-bucket
Amazon API GatewayのREST APIのIDを取得して
$ REST_API_ID=$(awslocal apigateway get-rest-apis --query 'items[0].id' --output text)
動作確認。
$ curl http://localhost:4566/restapis/$REST_API_ID/Prod/_user_request_/hello {"message":"Hello World!!"} $ curl http://localhost:4566/restapis/$REST_API_ID/Prod/_user_request_/hello?message=Middy {"message":"Hello Middy!!"}
OKですね。
少し、中身を見てみる
動くには動きましたが、middy
関数がなにをしているのかがちょっと気になります。
@middy/core
のソースコードを見ると、実質この1ファイルです。
https://github.com/middyjs/middy/blob/2.5.4/packages/core/index.js
ほぼ、ミドルウェアをAWS Lambdaのハンドラー関数に追加するだけのコードですね。
となると、今回のサンプルはミドルウェアを使っていないので、middyを使わずに書いた関数とほぼ差がないことになります。
middyのミドルウェアを使ったAWS Lambda関数を書く
このまま終わると面白くないので、middyのミドルウェアを使った関数を書いてみましょう。
お題としては、JSONでリクエストを送り、その中身を演算して返すものにしましょう。リクエストの内容に対する
バリデーションも行います。
主に使用するのは、以下のミドルウェアにします。
HTTPボディをJSONでパースするミドルウェアと、バリデーションを行うミドルウェアですね。
ドキュメントを読んでいると、http-json-body-parser
にはhttp-header-normalizer
を、validator
にはhttp-error-handler
を
合わせて使った方が良さそうなので、こちらも追加します。
$ mkdir calc $ cd calc $ npm init -y $ npm i -D typescript $ npm i -D -E prettier $ mkdir src
必要な依存関係を追加。
$ npm i @middy/core @middy/http-header-normalizer @middy/http-json-body-parser @middy/validator @middy/http-error-handler $ npm i -D @types/node@v14 @types/aws-lambda
依存関係は、最終的にはこうなりました。
"devDependencies": { "@types/aws-lambda": "^8.10.89", "@types/node": "^14.18.5", "prettier": "2.5.1", "typescript": "^4.5.4" }, "dependencies": { "@middy/core": "^2.5.4", "@middy/http-error-handler": "^2.5.4", "@middy/http-header-normalizer": "^2.5.4", "@middy/http-json-body-parser": "^2.5.4", "@middy/validator": "^2.5.4" }
tsconfig.js
と.prettierrc.json
、package.json
のscripts
の定義は最初と同じなので省略します。
ソースコードは、こんな感じで作成。
src/app.ts
import middy from '@middy/core'; import httpHeaderNormalizer from '@middy/http-header-normalizer'; import httpJsonBodyParser from '@middy/http-json-body-parser'; import httpErrorHandler from '@middy/http-error-handler'; import validator from '@middy/validator'; import { APIGatewayProxyEvent, APIGatewayProxyResult, Context, } from 'aws-lambda'; const schema = { type: 'object', required: ['body'], properties: { body: { type: 'object', required: ['operator', 'a', 'b'], additionalProperties: false, properties: { operator: { type: 'string', enum: ['+', '-'], }, a: { type: 'number', }, b: { type: 'number', }, }, }, }, }; type CalcRequest = { operator: string; a: number; b: number; }; const handler = async ( event: any, context: Context ): Promise<APIGatewayProxyResult> => { const calcRequest = event.body as CalcRequest; let result; switch (calcRequest.operator) { case '+': result = calcRequest.a + calcRequest.b; break; case '-': result = calcRequest.a - calcRequest.b; break; } return { statusCode: 200, body: JSON.stringify({ result: result, originalBody: event.rawBody, }), }; }; export const lambdaHandler = middy(handler) .use(httpHeaderNormalizer()) .use(httpJsonBodyParser()) .use(validator({ inputSchema: schema })) .use(httpErrorHandler());
関数は、リクエストの内容で加減算できるものにしました。
const handler = async ( event: any, context: Context ): Promise<APIGatewayProxyResult> => { const calcRequest = event.body as CalcRequest; let result; switch (calcRequest.operator) { case '+': result = calcRequest.a + calcRequest.b; break; case '-': result = calcRequest.a - calcRequest.b; break; } return { statusCode: 200, body: JSON.stringify({ result: result, originalBody: event.rawBody, }), }; };
リクエストは、こんな感じで送ります。
{"operator": "+", "a": 5, "b": 3}
これを、JSONパース → バリデーションとつなぐため、ミドルウェアの適用順はこうなります。
export const lambdaHandler = middy(handler) .use(httpHeaderNormalizer()) .use(httpJsonBodyParser()) .use(validator({ inputSchema: schema })) .use(httpErrorHandler());
http-json-body-parser
はContent-Type
ヘッダーがapplication/json
でないと機能しませんが、その前にHTTPヘッダーを
正規化しておくことが推奨されているため、http-header-normalizer
をその前に入れています。
ハンドラー関数に処理が来た時点では、JSONパースは終わっているのでそのままオブジェクトとして扱えます。
const calcRequest = event.body as CalcRequest;
パース前のオリジナルの値はrawBody
に入っているので、今回はこれも返すようにしました。
body: JSON.stringify({ result: result, originalBody: event.rawBody, }),
また、JSONパースができたあとは、中身をバリデーションすることができます。このため、http-json-body-parser
の後に
validator
が入ります。
最後のhttp-error-handler
は、バリデーションエラーが発生した時に400を返すことを目的に入れているミドルウェアです。
http-reponse-serializer
を使っている場合を除き、http-error-handler
は最後に登録する必要があります。
バリデーションの定義はこちらです。
const schema = { type: 'object', required: ['body'], properties: { body: { type: 'object', required: ['operator', 'a', 'b'], additionalProperties: false, properties: { operator: { type: 'string', enum: ['+', '-'], }, a: { type: 'number', }, b: { type: 'number', }, }, }, }, };
この内容は、validator
ミドルウェアに設定する必要があります。
※ちなみに、リクエストだけではなくレスポンスもバリデーションすることができます
.use(validator({ inputSchema: schema }))
バリデーションの定義はJSON Schemaで行い、バリデーションのエンジンとしてはAjvが使われています。
JSON Schema | The home of JSON Schema
JSON Schemaにはバージョンがあり、validator
プラグインはデフォルトではDraft 2019-09を使うAjvを利用するようなので
Default ajv plugins used: ajv-i18n, ajv-formats, ajv-formats-draft2019
Specification Links / Draft 2019-09 (formerly known as Draft 8)
JSON Schemaの書き方は、こちらを見ればよいことになります。
draft-handrews-json-schema-validation-02
あとはMakefile
を作成して
Makefile
build-CalcFunction: npm ci npm run build cp package*.json dist cd dist && \ npm ci --production && \ cp -R * ${ARTIFACTS_DIR}
AWS SAMのテンプレートにも定義を追加します。
Resources: HelloMiddyFunction: Type: AWS::Serverless::Function Properties: CodeUri: hello-middy/ Handler: app.lambdaHandler Runtime: nodejs14.x Architectures: - x86_64 Events: HelloMiddy: Type: Api Properties: Path: /hello Method: get Metadata: BuildMethod: makefile CalcFunction: Type: AWS::Serverless::Function Properties: CodeUri: calc/ Handler: app.lambdaHandler Runtime: nodejs14.x Architectures: - x86_64 Events: HelloMiddy: Type: Api Properties: Path: /calc Method: post Metadata: BuildMethod: makefile Outputs: HelloMiddyApi: Description: "API Gateway endpoint URL for Prod stage for Hello Middy function" Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/" CalcApi: Description: "API Gateway endpoint URL for Prod stage for Calc function" Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/calc/"
ビルドして、Amazon S3バケットを作成して、デプロイ。
$ cd .. $ samlocal build $ awslocal s3 mb s3://my-bucket $ samlocal deploy --stack-name my-stack --region us-east-1 --s3-bucket my-bucket $ REST_API_ID=$(awslocal apigateway get-rest-apis --query 'items[0].id' --output text)
確認。
$ curl -XPOST -H 'Content-type: application/json' http://localhost:4566/restapis/$REST_API_ID/Prod/_user_request_/calc -d '{"operator": "+", "a": 5, "b": 3}' {"result":8,"originalBody":"{\"operator\": \"+\", \"a\": 5, \"b\": 3}"} $ curl -XPOST -H 'Content-type: application/json' http://localhost:4566/restapis/$REST_API_ID/Prod/_user_request_/calc -d '{"operator": "-", "a": 8, "b": 4}' {"result":4,"originalBody":"{\"operator\": \"-\", \"a\": 8, \"b\": 4}"}
OKですね。
バリデーションが通らないリクエストを投げてみましょう。
$ curl -i -XPOST -H 'Content-type: application/json' http://localhost:4566/restapis/$REST_API_ID/Prod/_user_request_/calc -d '{"operator": "*", "b": "hoge"}' HTTP/1.1 400 Content-Type: text/plain Content-Length: 30 Access-Control-Allow-Origin: * Access-Control-Allow-Methods: HEAD,GET,PUT,POST,DELETE,OPTIONS,PATCH Access-Control-Allow-Headers: authorization,cache-control,content-length,content-md5,content-type,etag,location,x-amz-acl,x-amz-content-sha256,x-amz-date,x-amz-request-id,x-amz-security-token,x-amz-tagging,x-amz-target,x-amz-user-agent,x-amz-version-id,x-amzn-requestid,x-localstack-target,amz-sdk-invocation-id,amz-sdk-request Access-Control-Expose-Headers: etag,x-amz-version-id Connection: close date: Wed, 05 Jan 2022 11:14:12 GMT server: hypercorn-h11 Event object failed validation
結果は物悲しいですが、HTTPステータスコード400になりました。
ちなみに、http-error-handler
ミドルウェアを入れていない場合はこれで500(Internal Server Error)になります。
エラーの理由はログに出るようになっていて
$ awslocal logs tail /aws/lambda/my-stack-CalcFunction-f9323496
こんな感じになっています。
2022-01-05T11:14:12.825000+00:00 2022/01/05/[LATEST]758beacc 2022-01-05T11:14:12.817Z 241e7011-db28-10df-08c2-3a8f653e111c ERROR BadRequestError: Event object failed validation 2022-01-05T11:14:12.825000+00:00 2022/01/05/[LATEST]758beacc at createError (/var/task/node_modules/@middy/util/index.js:259:10) 2022-01-05T11:14:12.826000+00:00 2022/01/05/[LATEST]758beacc at validatorMiddlewareBefore (/var/task/node_modules/@middy/validator/index.js:53:21) 2022-01-05T11:14:12.827000+00:00 2022/01/05/[LATEST]758beacc at runMiddlewares (/var/task/node_modules/@middy/core/index.js:120:88) 2022-01-05T11:14:12.828000+00:00 2022/01/05/[LATEST]758beacc at async runRequest (/var/task/node_modules/@middy/core/index.js:80:5) { 2022-01-05T11:14:12.829000+00:00 2022/01/05/[LATEST]758beacc details: [ 2022-01-05T11:14:12.830000+00:00 2022/01/05/[LATEST]758beacc { 2022-01-05T11:14:12.831000+00:00 2022/01/05/[LATEST]758beacc instancePath: '/body', 2022-01-05T11:14:12.832000+00:00 2022/01/05/[LATEST]758beacc schemaPath: '#/properties/body/required', 2022-01-05T11:14:12.833000+00:00 2022/01/05/[LATEST]758beacc keyword: 'required', 2022-01-05T11:14:12.834000+00:00 2022/01/05/[LATEST]758beacc params: [Object], 2022-01-05T11:14:12.835000+00:00 2022/01/05/[LATEST]758beacc message: 'must have required property a' 2022-01-05T11:14:12.836000+00:00 2022/01/05/[LATEST]758beacc }, 2022-01-05T11:14:12.837000+00:00 2022/01/05/[LATEST]758beacc { 2022-01-05T11:14:12.838000+00:00 2022/01/05/[LATEST]758beacc instancePath: '/body/operator', 2022-01-05T11:14:12.838000+00:00 2022/01/05/[LATEST]758beacc schemaPath: '#/properties/body/properties/operator/enum', 2022-01-05T11:14:12.839000+00:00 2022/01/05/[LATEST]758beacc keyword: 'enum', 2022-01-05T11:14:12.840000+00:00 2022/01/05/[LATEST]758beacc params: [Object], 2022-01-05T11:14:12.841000+00:00 2022/01/05/[LATEST]758beacc message: 'must be equal to one of the allowed values' 2022-01-05T11:14:12.842000+00:00 2022/01/05/[LATEST]758beacc }, 2022-01-05T11:14:12.843000+00:00 2022/01/05/[LATEST]758beacc { 2022-01-05T11:14:12.844000+00:00 2022/01/05/[LATEST]758beacc instancePath: '/body/b', 2022-01-05T11:14:12.845000+00:00 2022/01/05/[LATEST]758beacc schemaPath: '#/properties/body/properties/b/type', 2022-01-05T11:14:12.846000+00:00 2022/01/05/[LATEST]758beacc keyword: 'type', 2022-01-05T11:14:12.847000+00:00 2022/01/05/[LATEST]758beacc params: [Object], 2022-01-05T11:14:12.848000+00:00 2022/01/05/[LATEST]758beacc message: 'must be number' 2022-01-05T11:14:12.849000+00:00 2022/01/05/[LATEST]758beacc } 2022-01-05T11:14:12.850000+00:00 2022/01/05/[LATEST]758beacc ] 2022-01-05T11:14:12.851000+00:00 2022/01/05/[LATEST]758beacc }
レスポンスにもある程度戻したいと思うのですが…今回はパスです。
また、JSONとしておかしい文字列を渡すと422になります。
$ curl -i -XPOST -H 'Content-type: application/json' http://localhost:4566/restapis/$REST_API_ID/Prod/_user_request_/calc -d '{"operator": ' HTTP/1.1 422 Content-Type: text/plain Content-Length: 61 Access-Control-Allow-Origin: * Access-Control-Allow-Methods: HEAD,GET,PUT,POST,DELETE,OPTIONS,PATCH Access-Control-Allow-Headers: authorization,cache-control,content-length,content-md5,content-type,etag,location,x-amz-acl,x-amz-content-sha256,x-amz-date,x-amz-request-id,x-amz-security-token,x-amz-tagging,x-amz-target,x-amz-user-agent,x-amz-version-id,x-amzn-requestid,x-localstack-target,amz-sdk-invocation-id,amz-sdk-request Access-Control-Expose-Headers: etag,x-amz-version-id Connection: close date: Wed, 05 Jan 2022 11:17:33 GMT server: hypercorn-h11 Content type defined as JSON but an invalid JSON was provided
これもエラーを発生させるのはhttp-json-body-parser
ですが、ハンドリングしているのはhttp-error-handler
なので、
こちらを入れておかないと500エラーになります。
とりあえず、こんな感じでしょうか。
まとめ
AWS Lambda向けのミドルウェアエンジン、middyを試してみました。
軽量でミドルウェアの組み合わせで扱えるので、Expressのような大きなフレームワークよりAWS Lambda環境に
向いているのかなと思います。
また、ひとつひとつのミドルウェアは小さいので、困ったらソースコードを見た方が確実に早いですね。
自分も今回書きながら何度も眺めることになりました。