Serverless + AWS Lambda + API Gatewayのヘッダマッピング
概要
AWS Lambda上でリクエストヘッダの取得とレスポンスヘッダの追加をServerless上で設定する方法がわからなくハマったので、それについてまとめました。
リクエストヘッダのマッピング
AWS Lambdaで生のリクエストヘッダを取得するためには、API Gateway上でマッピングテンプレートによりマッピングする必要があります。今回はいくつかのエンドポイントでヘッダ情報をチェックする必要があったため、こちらのドキュメントを参考に以下のように追記しました。
functions: hello: handler: header.hello events: - http: path: hello method: GET request: ${self:custom.request} custom: request: template: application/json: method: $context.httpMethod, body : $input.json('$'), headers: #foreach($param in $input.params().header.keySet()) $param: $util.escapeJavaScript($input.params().header.get($param)) #if($foreach.hasNext),#end #end
これでヘッダ情報が取得できるようになります。
export const hello = async (event, context) => { cosnole.log(event.headers); );
レスポンスヘッダのマッピング
今回、AWS Gatewayのデフォルトはapplication/json
のようで、これをapplication/json; charset=utf-8
に書き換える必要がありました。yaml上で解決したかったのですがうまく解決できず。今回はこちらを参考にコードに追記して解決しました。
export const hello = async (event, context) => ({ statusCode: 200, headers: { 'Content-Type': 'application/json; charset=utf-8', }, body: JSON.stringify({ message: 'Go Serverless v1.0! Your function executed successfully!', input: event, }), });
共通関数の作成
以下の目的のため、共通の関数を作成しました、ソースコードはこちらです。
header.ts
-> src/funcitons/example.ts
に移動し、serverless.yaml
の記述を書き換えます。
functions: hello: handler: src/functions/example.hello events: - http: path: hello method: GET
ディレクトリ構造は以下のような感じです。
|--package.json |--serverless.yml |--src | |--errors.ts | |--functions | | |--example.ts | | |--util.ts |--tsconfig.json |--yarn.lock
次にaws-lambda
の型定義を追加します。
$ yarn add -D @types/aws-lambda
共通関数は以下のように書きました。
import { Handler, Context, APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda'; import { CustomError } from '../errors'; interface HandlerResult { statusCode?: number; // defaultは200 body?: string | object; // JSON.stringifyするのでなんでもOK } type CustomHandler = ( event: APIGatewayProxyEvent, context: Context, ) => void | HandlerResult | Promise<HandlerResult>; export const createAPIGatewayHandler = (handler: CustomHandler): Handler => async ( event: APIGatewayProxyEvent, context: Context, ): Promise<APIGatewayProxyResult> => { try { // check headers const { statusCode = 200, body = '' } = (await handler(event, context)) || {}; return createResponse({ statusCode, body: JSON.stringify(body), // JSON.stringify(undefined)にならないようにbodyを初期化 }); } catch (error) { let { statusCode, message } = error; if (!(error instanceof CustomError)) { // unknownエラーは500エラー statusCode = 500; message = 'Internal Server Error'; } return createResponse({ statusCode, body: JSON.stringify({ message }), }); } }; const createResponse = (response: APIGatewayProxyResult): APIGatewayProxyResult => ({ ...response, headers: { 'Content-Type': 'application/json; charset=utf-8', ...response.headers, }, });
example.ts
は以下のようになり、event
, context
ともに型推論が利くので型定義は不要です。
import { createAPIGatewayHandler } from './util'; export const hello = createAPIGatewayHandler(event => ({ body: { message: 'Go Serverless v1.0! Your function executed successfully!', input: event, }, }));
まとめ
Lambda上でリクエストヘッダ情報のチェック・レスポンスヘッダの書き換えができるようになりました。認証のためのCustom Authorizersという機能もあるのでこちらもまたチェックしてみようと思います。