[AWS] Serverless FrameworkでRESTfulAPIを作成する (クエリー編)

AWSにServerless Frameworkを利用してRESTfulAPIを作成する際、クエリー文字列などの引数を取得する方法についてメモします。今回はAPI Gatewayのテンプレート関連の機能は使わず、Lambda側ですべて処理します。利用する言語はNode.jsです。

基本的な考え方

クエリーを受け取る

引数eventの中にqueryStringParametersというプロパティがありますので、これを参照するだけ。例えばURL/dev/hello?foo=barのクエリfooを参照したかったら以下のようにするだけです。

module.exports.echo1 = async event => {
  const foo = event.queryStringParameters.foo;
  // (略)
}

ただこれだけだとfooが渡されなかった場合に実行時エラーとなってしまうため、以下のようにチェックが必要です。

module.exports.echo1 = async event => {
  const query = event.queryStringParameters;
  const foo = ((query !== null) && ("foo" in query))?  query.foo:"fooが渡されませんでした";
  // (略)
}

クエリーが1件も渡されなかった場合はevent.queryStringParametersnullになります。

リクエストボディを受け取る

POSTなどで渡されたリクエストボディのデータを受け取りたい場合はevent.bodyを参照します。ただし特にパースやデコードが一切されていない生の文字列のまま入っていますので、扱いやすいように下準備をする必要があります。

application/json

以下の例ではJSON形式で渡されたリクエストボディをJavaScriptで扱えるようにパースを行っています。こちらもクエリーと同様にリクエストボディが空の状態で呼び出された場合event.bodynullになります。

module.exports.echo1 = async event => {
  const body = JSON.parse(event.body);
  const foo = ((body !== null) && ("foo" in body))?  body.a:"fooが渡されませんでした";
  // (略)
}

x-www-form-urlencoded

JSON形式ではなくa=123&b=456といった形式の文字列を扱う場合には、自前でデコード&パース処理を書いても良いのですが既存のモジュールを使ってあげると手早くできます。

というわけでquery-stringモジュールをインストール。

$ npm install query-string

以下のようにモジュールを読み込みevent.bodyをparseしてあげるだけです。

const queryString = require("query-string");

module.exports.echo2 = async event => {
  const body = queryString.parse(event.body)
  // 略
}

実際に試している

準備

今回は「echo」という名前のプロジェクトを作成しました。

$ serverless 
Serverless: No project detected. Do you want to create a new one? Yes
Serverless: What do you want to make? AWS Node.js
Serverless: What do you want to call this project? echo

ディレクトリが作成されているはずですので、中に入ってnpm initしておきます。

$ cd echo
$ npm init

このあたりの詳細は過去記事を参照ください。 blog.katsubemakito.net

サンプルコード

serverless.yml

プロジェクトの設定を行います。URL/(stage)/echoをGETでリクエストするとecho1を、POSTでリクエストするとecho2を実行するよう設定しています。

service: echo
provider:
  name: aws
  runtime: nodejs12.x
functions:
  echo1:
    handler: handler.echo1
    events:
      - http:
          path: echo
          method: get
  echo2:
    handler: handler.echo2
    events:
      - http:
          path: echo
          method: post

handler.js

実際のコードです。handler.jsの中に以下の2つの関数をそのまま貼り付けます。

'use strict';

/**
 * GET /(stage)/echo?foo=xxxxx
 */
module.exports.echo1 = async event => {
  const query = event.queryStringParameters;
  const foo = ((query !== null) && ("foo" in query))?  query.foo:"foo is empty";

  return {
    statusCode: 200,
    body: JSON.stringify(
      {
        message: 'Im echo1',
        query: foo,
      },
      null,
      2
    ),
  };
};


/**
 * POST /(stage)/echo/
 * foo=xxxxx
 */
module.exports.echo2 = async event => {
  const body = JSON.parse(event.body)
  const foo = ((body !== null) && ("foo" in body))?  body.foo:"foo is empty";

  return {
    statusCode: 200,
    body: JSON.stringify(
      {
        message: 'Im echo2',
        body: foo,
      },
      null,
      2
    ),
  };
};

デプロイ

deployコマンドでAWSへ反映します。

$ serverless deploy

途中でURLが表示されるのでこれをメモします。

endpoints:
  GET - https://38unnrqr5b.execute-api.us-east-1.amazonaws.com/dev/echo
  POST - https://38unnrqr5b.execute-api.us-east-1.amazonaws.com/dev/echo

実行する

ではcurlコマンドで挙動を確認します。まずはGETでクエリー文字列を取得します。

$ curl 'https://38unnrqr5b.execute-api.us-east-1.amazonaws.com/dev/echo?foo=12345'
{
  "message": "Im echo1",
  "query": "12345"
}

次にPOSTでリクエストボディの値を取得します。

$ curl -X POST \
-H "Content-Type: application/json" \
-d '{"foo":67890}' \
'https://38unnrqr5b.execute-api.us-east-1.amazonaws.com/dev/echo'

{
  "message": "Im echo2",
  "body": 67890
}

参考ページ