LoginSignup
22
31

More than 5 years have passed since last update.

APIGatewayで使うLambda用の関数を作る

Last updated at Posted at 2016-11-25

前提

Lambdaプロキシ統合を使用(LAMBDA_PROXY, Lambda Proxy)
→APIGatewayの統合リクエストで「Lambda プロキシ統合の使用」をチェック。

テンプレート(とりあえず早く使いたい人用)

'use strict';

//External Libraries
//const hoge = require("hoge");

module.exports.functionname = (event, context, callback) => {
  //Respomse Template
  var response = {
    statusCode: 200,
    headers: {"Access-Control-Allow-Origin" : "*"},
    body: null
  };

  //Response.Body Template
  var body = {
    message: 'successfully!',
    input: event,
    results: null
  };

  try {
    var results = '';

    body.results = results;
    response.body = JSON.stringify(body);
    callback(null, response);
  } catch(e) {
    //Exception
    response.statusCode = 500;
    body.message = e.message;
    response.body = JSON.stringify(body);

    callback(null, response);
  } finally {

  }
};

基本的な記述(serverlessより)

例:handler.json

'use strict';

module.exports.hello = (event, context, callback) => {
  const response = {
    statusCode: 200,
    headers: {"Access-Control-Allow-Origin" : "*"},
    body: JSON.stringify({
      message: 'successfully!',
      input: event,
    }),
  };

  callback(null, response);
};

レスポンスの注意

Lambdaプロキシ統合を使用している場合、
「statusCode」
「headers」
「body」
は必須になるので注意。

※入れないと、サーバエラーになります
※bodyの値は文字列じゃないとダメ

LambdaでCORSを有効にするには

そもそもCORSってなによって方はこちらへ↓
CORS簡易まとめ

このページでは、前提として「Lambdaプロキシ統合」があるので、
レスポンスのヘッダ情報に
「"Access-Control-Allow-Origin" : "*"」
を加えるだけ。

解説:module.exports.関数名

まず、この記述の仕方は何か。
→この関数が、外部モジュールとして読み込まれたときの使い方を設定している。

深く考えたくなかったら↓

こういうものだと思って関数を定義すれば良い。

よく知りたいなら↓

外部モジュールは

var hoge = require('ファイル名');

で読み込まれる。(Node.jsの場合)

requireの戻り値を設定するために、
呼ばれた関数では「exports」か「module.exports」で定義しなければならない。

違いは、

説明
exports オブジェクトとして返す。
複数のプロパティを内包させたいときに使う。
module.exports オブジェクトをセットした変数?を返す。
クラスや単一のオブジェクトをrequireさせたいときに使う。

解説:event

Lambdaが受け取るリクエストパラメータのようなもの。
Lambdaと紐付けるサービスによって、変幻自在。

API Gatewayの場合、
統合リクエストのタイプ「LAMBDA_PROXY」を使うと手早くすすめるので、
それを使いましょう。

※マッピングテンプレートでせかせか作るのはプロにまかせましょう

よく使う情報の参照方法

1. クエリパラメータ

event.queryStringParameters.パラメータ

※注意
クエリパラメータが皆無の場合、「event.queryStringParameters = null」となり、
上記参照方法も例外となる。
状況に応じては、

if(event.queryStringParameters !== null && event.queryStringParameters !== undefined) {
  if(event.queryStringParameters.id !== null && event.queryStringParameters.id !== undefined) {
    id = event.queryStringParameters.id;
  }
 }

↑こんなチェックも必要かも。

2. POSTパラメータ

event.body

※注意
POSTパラメータは文字列で必ず渡ってくる。
つまり、

# 通常形式
id=1&name=hoge

# JSON形式
{id:1, name:'hoge'}

もLambda内では一度使いやすいようにパースしてあげる必要がある。

ゆるふわな方法は後述

(serverless)ローカルでeventに情報を渡す

  1. 事前にjsonファイルで渡すデータを作っておく。
event.json
{
"queryStringParameters": {
  "id": 2
}
}

2.invokeするときに、作ったjsonファイルを読み込み

sls invoke local -f 関数名 -p jsonファイル

eventの中身

eventの中身(20161125時点)
{
"resource": "/index",
"path": "/index",
"httpMethod": "GET",
"headers": null,
"queryStringParameters": null,
"pathParameters": null,
"stageVariables": null,
"requestContext": {
  "accountId": "xxxxxxxxxxxx",
  "resourceId": "xxxxxx",
  "stage": "test",
  "requestId": "xxxxxxxxxxxxxxxxxxx",
  "identity": {
    "cognitoIdentityPoolId": null,
    "accountId": "xxxxxxxxxxxx",
    "cognitoIdentityId": null,
    "caller": "xxxxxxxxxxxx",
    "apiKey": "xxxxxxxxxxxxxxxxxxx",
    "sourceIp": "xxxxxxxxxxxxxxxxxxxxx",
    "accessKey": "xxxxxxxxxxxxxxxxxxxx",
    "cognitoAuthenticationType": null,
    "cognitoAuthenticationProvider": null,
    "userArn": "arn:aws:iam::xxxxxxxxxxxx:xxxx",
    "userAgent": "Apache-HttpClient/4.5.x (Java/1.8.0_102)",
    "user": "xxxxxxxxxxxx"
  },
  "resourcePath": "/index",
  "httpMethod": "GET",
  "apiId": "xxxxxxxxxx"
},
"body": null,
"isBase64Encoded": false
}

解説:context

ランタイム情報を格納している。
使用メモリ数くらいは使えそう。

解説:callback

LambdaのNode4.3からサポートが追加されたもの。

ファンクションを明示的に終了するために使われる。

正直、どういう挙動をするものなのかまだよく理解できていない。

## 定義
callback(Error err, Object response);

## 関数実行終了
callback();

## 関数の実行に失敗
callback(err);

## 関数の実行に成功
callback(null, response);
引数 説明
Error err 失敗時にErrorオブジェクトにその内容がセットされる
Object response 成功時の返却値
JSONにしないとだめ

JSON.stringify(value[, replacer[, space]])

指定した値をJSON形式に変換してくれる。

何も考えたくなければ、

JSON.stringify(value, null, 2);

が良いかと思う。(フィルタなし、半角スペース2でインデント整形)

引数 説明
value JSONに変換したいものを指定
replacer 指定した関数か配列でフィルタをかける

関数→引数key,valueでフィルタ処理をかける
配列→指定値をkeyとして、それをフィルタ(削除)
space 結果JSONのインデントを整えてくれる。

数値なら半角スペースで(最大10),文字列ならそれをインデント文字として使う(タブ文字なら\tなど)

外部ライブラリをいれよう

mysql2モジュールを入れてみる
npm install mysql2 --save-dev

※node_modulesディレクトリに配置される

handler.jsonで読み込んでみる
'use strict';

const mysql = require('mysql2');

module.exports.hello = (event, context, callback) => {
~

mysql2を使ってみよう

設定ファイルを準備

mkdir config && touch config/db.json
cat << EOS >> config/db.json
{
  "name": {
    "host": "localhost",
    "user": "root",
    "password": "pass",
    "database": "db_name"
  }
}
EOS

処理を記載

handler.json
'use strict';

const db = require("./config/db");
const mysql = require('mysql2');

module.exports.hello = (event, context, callback) => {
  var response = {
    statusCode: 200,
    headers: {"Access-Control-Allow-Origin" : "*"},
    body: null
  };

  var body = {
    message: 'successfully!',
    input: event,
    results: null
  };

  var con = null;

  try {
    //[1]コネクション作成
    con = mysql.createConnection(db.zpms);

    //[2]クエリ実行
    con.query('SELECT * FROM tb_test', (err, results, fields) => {
   //[3]結果をレスポンスに
      body.results = (err === null) ? results : err;
      response.body = JSON.stringify(body);

   //[4]完了
      callback(null, response);
    });

  } catch(e) {
    response.statusCode = 500;
    body.message = e.message;
    response.body = JSON.stringify(body);

    callback(null, response);
  } finally {
  //[5]ちゃんとコネクションは閉じること
    con.end();
  }
};

※con.endを明示的にしないと、Lambdaの実行が終わらない(タイムアウトまで)

※プリペアードステートメントで

  try {
    var first = 1;
    var second = 2;

    con = mysql.createConnection(db.zpms);

    con.query('SELECT * FROM tb_test WHERE id IN (?, ?)', [first , second ], (err, results, fields) => {
      body.results = (err === null) ? results : err;
      response.body = JSON.stringify(body);

      callback(null, response);
    });

  } catch(e) {
    ~

POSTパラメータをパースしよう

バリデータとパーサーをインスコ

npm install validator qs --save-dev

参考
validator
qs

処理

const validator = require('validator');
const qs = require('qs');

~

var pParam = null;

if(event.body !== null) {
  pParam = (validator.isJSON(event.body)) ? JSON.parse(event.body) qs.parse(event.body);
}
22
31
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
31