ServerlessFramework のプラグインである serverless-rust を使用して、API GatewayとAWS Lambdaを使ったAPIを作成しようとしていた。
Rustでラムダ関数を作成するのに便利なクレートはないだろうか?と調べたところ、lambda_runtime なるものを見つけた。
おおこれは便利だ、とサンプルコードをそのまま使用してみた。何かあれば修正してデプロイすれば良いし、無料枠を潰すほど実行することもないだろう。できればデプロイにかかる時間を有料枠で買えればよかったが...
紆余曲折あったものの、serverlessコマンドで無事APIを作成することはできたようだ。
試しに実行してみよう。
$ curl https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/test
{"message": "Internal server error"}
あら、やっぱりだめか。エラー内容を確認するためにCloudWatch Logsで確認してみたところ、ラムダ関数内でエラーはなく無事に実行完了していた。
なるほど、APIGatewayに渡すならレスポンスの形はちゃんと整えなければならないようだ。ドキュメントによると Serialize
を実装していればよいらしい。
とりあえず json!
マクロでそれっぽいものを作ってみよう。
async fn handler(
_event: LambdaEvent<serde_json::Value>,
) -> Result<serde_json::Value, lambda_runtime::Error> {
Ok(json!({
"statusCode": 200,
"body": "OK",
})
}
デプロイして実行してみたところ
$ curl https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/test
{"message":"OK"}
うまくいったみたい。やったね。
ただせっかくレスポンスが「Serialize
を実行している型」と定義されているなら、そんなレスポンスオブジェクトを作ってしまおう。
自分はこういう何でもありなオブジェクトはタイポや名前忘れが防げなくてキライだ。Rustのコンパイラにちゃんとチェックしてもらいたい。
作りました
こいつです → lambda-apigateway-response
実際の実装はこんな感じ。シンプルイズベスト
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Response<T: Serialize> {
#[serde(serialize_with = "serialize_status_code")]
pub status_code: StatusCode,
#[serde(serialize_with = "serialize_body")]
pub body: T,
#[serde(skip_serializing_if = "Headers::is_empty")]
pub headers: Headers,
#[serde(skip_serializing_if = "MultiValueHeaders::is_empty")]
pub multi_value_headers: MultiValueHeaders,
pub is_base64_encoded: bool,
}
まだできたてなのでいろいろと足りていないところがあるけれど、そこはご容赦を。
ビルダーパターンを取り入れても良いし、Serialize
を実装していない lambda_http::Response
から変換できるよう From<lambda_http::Response>
を実装してもよいかもしれない?
頑張ろう💪