API GatewayとAWS Lambda(Node.js)を使いQRコードを生成するAPIを作成する その2
こんにちは。
株式会社フルーデンスの小巻です。
今回はタイトルの通り、API GatewayとAWS Lambda(Node.js)を使いQRコードを生成するAPIを作成する方法を書きます。
更新履歴
2023年9月25日
2023年9月25日に、以下の記事を公開しました。
簡易的にQRコードを生成できるようになりましたので、興味がありましたら、ご確認ください。
FileMakerからQRコードを生成するためのAPIを公開しました | フルーデンス
以前、API GatewayとAWS Lambda(Node.js)を使い効率よくWebサービスと連携する その1という記事を書きましたので、その続編になります。
内容は以下の通りです。
目次
- ローカルでコードを書く
- AWS Lambda レイヤーを活用する
- AWS Lambdaで関数を作成する
- Amazon API GatewayでAPIを作成する
- Amazon API GatewayのAPIキーを活用する
環境
- macOS Catalina 10.15.6
- FileMaker Pro 19.1.2.219
前提
- AWSのアカウントを作成していること
- nodeをインストールしていること
少しコードを書きますが、簡単なコードなので、興味がある方は是非とも手を動かして頂ければと思います。
私もNode.jsを勉強中のため、コードに誤りがあったり、より良いコードがあるようでしたらご指摘頂ければと思います。
API化するメリット
- オンラインであれば、URLから挿入ステップで簡単に作成できる。
- (開発者として考えると)さまざまなソリューションで使い回せる。
QRコード生成のイメージ
今回のブログの作業が完了すると、FileMakerから簡単にQRコードを生成できます。
// # --- sample param // # { "text": "FileMaker開発者でVimを使っている人はいますか?", "options": { "type": "png", "margin": 0, "scale": 4, "color": { "dark": "#000", "light": "#0000" } } } # # --- set param 変数を設定 [ $param ; 値: "" ] 変数を設定 [ $param ; 値: JSONSetElement ( $param ; "text" ; qrcode::text ; JSONString ) ] 変数を設定 [ $param ; 値: JSONSetElement ( $param ; "options.type" ; qrcode::options_type ; JSONString ) ] 変数を設定 [ $param ; 値: JSONSetElement ( $param ; "options.margin" ; qrcode::options_margin ; JSONNumber ) ] 変数を設定 [ $param ; 値: JSONSetElement ( $param ; "options.scale" ; qrcode::options_scale ; JSONNumber ) ] 変数を設定 [ $param ; 値: JSONSetElement ( $param ; "options.color.dark" ; qrcode::options_color_dark ; JSONString ) ] 変数を設定 [ $param ; 値: JSONSetElement ( $param ; "options.color.light" ; qrcode::options_color_light ; JSONString ) ] # 変数を設定 [ $debug_url ; 値: api_qrcode_url ] 変数を設定 [ $debug_option ; 値: api_qrcode_option ( api_qrcode_key ; $param ) ] # # --- curl 変数を設定 [ $result ; 値: "" ] URL から挿入 [ 選択 ; ダイアログあり: オフ ; ターゲット: $result ; api_qrcode_url ; SSL 証明書の検証 ; cURL オプション: api_qrcode_option ( api_qrcode_key ; $param ) ] # # --- qrcode result 変数を設定 [ $statusCode ; 値: JSONGetElement ( $result ; "statusCode" ) ] 変数を設定 [ $body ; 値: JSONGetElement ( $result ; "body" ) ] If [ $statusCode = 200 ] # --- ok フィールド設定 [ qrcode::qrcode ; Base64Decode ( $body ; "qrcode.png" ) ] Else # --- error 警告音 カスタムダイアログを表示 [ "error" ; $body ] End If # 現在のスクリプト終了 [ テキスト結果: ]
"https://api.frudens.com/test-qrcode/"
Let ( $____body____ = _body ; Substitute ( List ( "--request POST " ; "--header 'x-api-key: __key__' " ; "--data-binary @$____body____ " ) ; /*list*/ [ _singleQuotation_ ; _doubleQuotation_ ] ; [ "__key__" ; _key ] ) /*substitute*/ ) /*let*/
オブジェクトフィールドをエクスポートして…
ターミナルで確認します。
(ins) teruhiro@MacBook-Pro-13:~ $ zbarimg Desktop/qrcode.png QR-Code:FileMaker開発者でVimを使っている人はいますか? scanned 1 barcode symbols from 1 images in 0.01 seconds . EAN/UPC (EAN-13, EAN-8, EAN-2, EAN-5, UPC-A, UPC-E, ISBN-10, ISBN-13) . DataBar, DataBar Expanded . Code 128 . Code 93 . Code 39 . Codabar . Interleaved 2 of 5 . QR code . SQ code - is the barcode large enough in the image? - is the barcode mostly in focus? - is there sufficient contrast/illumination? - If the symbol is split in several barcodes, are they combined in one image? - Did you enable the barcode type? some EAN/UPC codes are disabled by default. To enable all, use: $ zbarimg -S*.enablePlease also notice that some variants take precedence over others. Due to that, if you want, for example, ISBN-10, you should do: $ zbarimg -Sisbn10.enable
これから先は、macOSでターミナルを使う作業になります。
Windowsの方は、WSL2を有効にして頂き、Linux環境を準備して頂ければ、ほぼ同じコマンドで実行できると思います。
また Node.js がインストールされている前提で進めます。
ちなみに、私は Node.js のインストールには、nodebrewを使っています。
https://github.com/hokaccha/nodebrew
ローカルでコードを書く
ディレクトリを作成する
ターミナルを起動します。
作業用のディレクトリを作成します。
(ins) teruhiro@MacBook-Pro-13:~ $ node -v v12.18.3 (ins) teruhiro@MacBook-Pro-13:~ $ npm -v 6.14.8 (ins) teruhiro@MacBook-Pro-13:~ $ pwd /Users/teruhiro (ins) teruhiro@MacBook-Pro-13:~ $ mkdir -p ~/tmp/test_qrcode (ins) teruhiro@MacBook-Pro-13:~ $ cd ~/tmp/test_qrcode (ins) teruhiro@MacBook-Pro-13:~/tmp/test_qrcode $ npm init -y Wrote to /Users/teruhiro/tmp/test_qrcode/package.json: { "name": "test_qrcode", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }
パッケージをインストールする
今回利用するパッケージは qrcode です。
https://github.com/soldair/node-qrcode
パッケージをインストールします。
(ins) teruhiro@MacBook-Pro-13:~/tmp/test_qrcode $ npm install --save qrcode npm notice created a lockfile as package-lock.json. You should commit this file. npm WARN test_qrcodee@1.0.0 No description npm WARN test_qrcode@1.0.0 No repository field. + qrcode@1.4.4 added 37 packages from 28 contributors and audited 37 packages in 1.58s 1 package is looking for funding run `npm fund` for details found 0 vulnerabilities
好きなエディタを起動して編集する
コードを書くために index1.js というファイルを作成します。
(修正しdiffを見るために index1.js というファイル名にしています。)
(ins) teruhiro@MacBook-Pro-13:~/tmp/test_qrcode $ touch index1.js (ins) teruhiro@MacBook-Pro-13:~/tmp/test_qrcode $ ls -l total 32 -rw-r--r-- 1 teruhiro staff 0 Sep 30 23:55 index1.js drwxr-xr-x 40 teruhiro staff 1280 Sep 30 23:55 node_modules -rw-r--r-- 1 teruhiro staff 10662 Sep 30 23:55 package-lock.json -rw-r--r-- 1 teruhiro staff 284 Sep 30 23:55 package.json
index1.js を好きなエディタで開いてください。
私はVimが好きなので、Vimで開きます。
(ins) teruhiro@MacBook-Pro-13:~/tmp/test_qrcode $ vim index1.js
まずは、リポジトリのREADMEのサンプルを参考に、以下のコードを貼り付けて実行してみます。
toFile(path, text, [options], [cb(error)])
https://github.com/soldair/node-qrcode#tofilepath-text-options-cberror
const qrcode = require('qrcode') const options = { color: { dark: '#000', light: '#0000' }, margin: 0 } qrcode.toFile('tmp.png', 'I like Vim.', options, err => { if (err) throw err console.log('done') })
QRコードが生成できるか確認する
ターミナルを開き、コードを実行します。
(ins) teruhiro@MacBook-Pro-13:~/tmp/test_qrcode $ ls -l total 48 -rw-r--r-- 1 teruhiro staff 224 Sep 30 23:38 index1.js drwxr-xr-x 40 teruhiro staff 1280 Sep 29 19:07 node_modules -rw-r--r-- 1 teruhiro staff 10655 Sep 29 19:07 package-lock.json -rw-r--r-- 1 teruhiro staff 277 Sep 29 19:07 package.json (ins) teruhiro@MacBook-Pro-13:~/tmp/test_qrcode $ node index1.js done (ins) teruhiro@MacBook-Pro-13:~/tmp/test_qrcode $ ls -l total 56 -rw-r--r-- 1 teruhiro staff 224 Sep 30 23:38 index1.js drwxr-xr-x 40 teruhiro staff 1280 Sep 29 19:07 node_modules -rw-r--r-- 1 teruhiro staff 10655 Sep 29 19:07 package-lock.json -rw-r--r-- 1 teruhiro staff 277 Sep 29 19:07 package.json -rw-r--r-- 1 teruhiro staff 580 Oct 1 00:00 tmp.png
tmp.pngが生成されましたので、確認します。
openコマンドでファイルを開くか、もしくは、ファインダーをひらきます。
(ins) teruhiro@MacBook-Pro-13:~/tmp/test_qrcode $ open tmp.png
もしくは…
(ins) teruhiro@MacBook-Pro-13:~/tmp/test_qrcode $ open .
QRコードの読み取りは、手元のiPhoneやAndroidのカメラをアプリを使って確認してください。
ターミナルで確認したい!という方は、brewで zbar をインストールしてください。
https://formulae.brew.sh/formula/zbar
生成したpngをbase64エンコードする
生成したtmp.pngをbase64エンコードしたいので、複製しコードを修正します。
(cmd) teruhiro@MacBook-Pro-13:~/tmp/test_qrcode $ cp index1.js index2.js (cmd) teruhiro@MacBook-Pro-13:~/tmp/test_qrcode $ vim index2.js
以下のように修正します。
const qrcode = require('qrcode') const fs = require('fs') const options = { color: { dark: '#000', light: '#0000' }, margin: 0 } qrcode.toFile('tmp.png', 'I like Vim.', options, err => { if (err) throw err // console.log('done') }) fs.readFile('tmp.png', (err, data) => { if (err) throw err const textEnc = data.toString('base64') console.log(textEnc) })
vimdiffコマンドで確認します。
確認します。
(ins) teruhiro@MacBook-Pro-13:~/tmp/test_qrcode $ node index2.js iVBORw0KGgoAAAANSUhEUgAAAFQAAABUCAYAAAAcaxDBAAAAAklEQVR4AewaftIAAAICSURBVO3BUYprSQwFwUxx97/lM78WDdUUVttvQBFCwkcpTcIV5UrCBxVrVLFGFWvUww/KqIQrylHCkXKkjEp4UaxRxRpVrFEPv0q4olxRjhL+VMIV5aBYo4o1qlijHr4uoVEapUn4hxVrVLFGFWvUw9cpTUKjHCn/kGKNKtaoYo16+JXyT0tolCvKoGKNKtaoYo16+CHhoxIapUlolCbhSsIfKtaoYo0q1ij5uoQjpUk4Ur6oWKOKNapYox5IaJQm4UhpEq4oRwlvSWiUo4RGaRIOijWqWKOKNUp+ldAoTUKjHCU0ypWEK8pbEhqlSXhRrFHFGlWsUQ8kNMpRwpWEtyQcKVcSGqVJaJQLxRpVrFHFGiUkNMpbEhplVEKjvCXhSLlQrFHFGlWsUfJDQqMcJfwp5SihUZqEI+UtCS+KNapYo4o1Sr4uoVGahCPlLQmN0iQ0SpPwolijijWqWKOEhI9SmoRGOUo4Uq4kNMobijWqWKOKNerhB2VUwlsSGuUooVGOlCbhDcUaVaxRxRr18KuEK8oV5S0JjXKU0ChXlINijSrWqGKNevi6hEa5ohwlNMqR0iQ0CY3yolijijWqWKMe/vcSjpQrCUfKQbFGFWtUsUY9/Er5U0qTcJRwpDQJV5QrCS+KNapYo4o1Skj4KKVJ+CjlSkKjHBRrVLFGFWvUfzK6mrU252RdAAAAAElFTkSuQmCC
tmp.pngをbase64エンコードできました。
デコードして、同じファイルが生成できるか確認します。
(ins) teruhiro@MacBook-Pro-13:~/tmp/test_qrcode $ node index2.js | base64 -D > tmp2.png (ins) teruhiro@MacBook-Pro-13:~/tmp/test_qrcode $ ls -l total 88 drwxr-xr-x 11 teruhiro staff 352 Oct 3 23:08 . drwxr-xr-x 65 teruhiro staff 2080 Oct 2 17:03 .. -rw-r--r-- 1 teruhiro staff 224 Oct 1 00:06 index1.js -rw-r--r-- 1 teruhiro staff 382 Oct 1 00:14 index2.js drwxr-xr-x 40 teruhiro staff 1280 Sep 29 19:07 node_modules -rw-r--r-- 1 teruhiro staff 10655 Sep 29 19:07 package-lock.json -rw-r--r-- 1 teruhiro staff 277 Sep 29 19:07 package.json -rw-r--r-- 1 teruhiro staff 580 Oct 3 23:07 tmp.png -rw-r--r-- 1 teruhiro staff 580 Oct 3 23:08 tmp2.png
複製しコードを修正します。
(cmd) teruhiro@MacBook-Pro-13:~/tmp/test_qrcode $ cp index2.js index3.js (cmd) teruhiro@MacBook-Pro-13:~/tmp/test_qrcode $ vim index3.js
const qrcode = require('qrcode') const tosjis = require('qrcode/helper/to-sjis') const fs = require('fs') const path = 'tmp.png' const eventParams = { text: 'I like Vim.', options: { type: 'png', margin: 0, scale: 4, color: { dark: '#000', light: '#0000' } } } async function qrcodeToFile (params) { return new Promise((resolve, reject) => { params.options.toSJISFunc = tosjis qrcode.toFile(path, params.text, params.options, err => { if (err) reject(err) resolve('qrcode created.') }) }) } async function pngToString () { return new Promise((resolve, reject) => { fs.readFile(path, (err, data) => { if (err) reject(err) const textEnc = data.toString('base64') resolve(textEnc) }) }) } async function main (event) { try { const toFile = await qrcodeToFile(event) console.log(toFile) const toString = await pngToString() // response const response = { statusCode: 200, body: toString } console.log(response) return response } catch (e) { console.error(e) // response const response = { statusCode: 400, body: e.message } console.log(response) return response } } main(eventParams)
実行します。
(ins) teruhiro@MacBook-Pro-13:~/tmp/test_qrcode $ node index3.js qrcode created. { statusCode: 200, body: 'iVBORw0KGgoAAAANSUhEUgAAAFQAAABUCAYAAAAcaxDBAAAAAklEQVR4AewaftIAAAHNSURBVO3BUaojUQxDwSPT+9+y5jcm4HBppfMGXCXAPEt05ow4Yx5UrKhiRRUr6uKdyDJnxMzMxExkmRfFiipWVLGiLj4zZ8QZMTPfZc6IQbGiihVVrKiL3zOd6ERn/rBiRRUrqlhRF78nOtOJmfhDihVVrKhiRV18Jv4204kzIqhYUcWKKlbUxTvzLNOJznSiM2fMFxUrqlhRxYoSv2dmojMz8UPFiipWVLGiLsB0ojMz0ZkzYmbuMZ2YmU50ZlCsqGJFFStKfGY60ZlOzEwnzpgz4h7Tic68KFZUsaKKFXUBphMzc8bcY2bijOlEZzpxoFhRxYoqVpQA04l7TCeyTCfuMTNxoFhRxYoqVpR4ZzoxM98lZqYTnZmJe8yLYkUVK6pYUeL3TCc6MxP3mE50phOdeVGsqGJFFStKgHmW6EwnZmYmzphO3FCsqGJFFSvq4p3IMveYTsxMJ2aiMzcUK6pYUcWKuvjMnBFnxD2mEzPTiTNiUKyoYkUVK+ri90wnzoiZ6cRMdKYznXhRrKhiRRUr6uL/Z2bijJmJQbGiihVVrKiLz8R3ic7MzEx05ow4Y14UK6pYUcWKEmCeJTrzLHHGdGJQrKhiRRUr6h8/nk24a0yfCgAAAABJRU5ErkJggg==' }
AWS Lambda レイヤーを活用する
今までのコードをLambdaに貼り付けて、テストしたいところですが、Lambdaには ローカルでインストールしたqrcodeパッケージがありません。
そのため、Lambdaのレイヤーを活用します。
以下のURLに記載の内容の一部を修正し記載します。
レイヤーを使用して、最新バージョンの AWS SDK for JavaScript を Node.js Lambda 関数に統合する方法を教えてください。
qrcodeパッケージをレイヤーで利用するためのコード
1.作業ディレクトリを作成します。 mkdir -p ~/tmp/qrcode-layer/nodejs 2.作業ディレクトリに変更します。 cd ~/tmp/qrcode-layer/nodejs 3.AWS SDK の最新バージョンをインストールします。 npm install qrcode 4.レイヤーにアップロードする .zip ファイルを作成します。 zip -r ../package.zip ../
zipの準備ができました。
Lambda レイヤーを作成する
AWSにログインし、Lambda > レイヤーをクリックします。
アップロードから、作成した package.zip を選択し、レイヤーを作成します。
作成できました。
AWS Lambdaで関数を作成する
関数を作成します。
今回は test-qrcode という名前にしました。
レイヤーを設定する
レイヤーを選択し、追加します。
コードを貼り付ける
デフォルトで以下のコードが設定されています。
exports.handler = async (event) => { // TODO implement const response = { statusCode: 200, body: JSON.stringify('Hello from Lambda!'), }; return response; };
先ほど、作成したコードを貼り付けますが、Lambdaで実行するため一部を修正します。
以下のコードを貼り付けます。
const qrcode = require('qrcode') const tosjis = require('qrcode/helper/to-sjis') const fs = require('fs') const path = '/tmp/tmp.png' async function qrcodeToFile (params) { return new Promise((resolve, reject) => { params.options.toSJISFunc = tosjis qrcode.toFile(path, params.text, params.options, err => { if (err) reject(err) resolve('qrcode created.') }) }) } async function pngToString () { return new Promise((resolve, reject) => { fs.readFile(path, (err, data) => { if (err) reject(err) const textEnc = data.toString('base64') resolve(textEnc) }) }) } exports.handler = async event => { try { const toFile = await qrcodeToFile(event) console.log(toFile) const toString = await pngToString() // response const response = { statusCode: 200, body: toString } console.log(response) return response } catch (e) { console.error(e) // response const response = { statusCode: 400, body: e.message } console.log(response) return response } }
貼り付けたら Deploy をクリックします。
vimdiffで確認します。
テストイベントを設定する
コードの引数として使っていたオブジェクトをテストイベントにJSONとして貼り付けます。
const eventParams = { text: 'I like Vim.', options: { type: 'png', margin: 0, scale: 4, color: { dark: '#000', light: '#0000' } } }
{ "text": "I like Vim.", "options": { "type": "png", "margin": 0, "scale": 4, "color": { "dark": "#000", "light": "#0000" } } }
テストをクリックします。
ローカルと同じ結果になればOKです。
これで、Lambdaは設定できましたので、API Gatewayを設定します。
Amazon API GatewayでAPIを作成する
API Gatewayをクリックします。
APIを作成します。
作成方法の詳細は API GatewayとAWS Lambda(Node.js)を使い効率よくWebサービスと連携する その1 という記事に記載していますので、分からないかたは参照してください。
テストします。
デプロイする
以下のURLが発行されました。
このURLにPOSTすれば、関連付けたLambdaを実行できるようになります。
https://j6z468o1r3.execute-api.us-east-2.amazonaws.com/prod
デプロイについても API GatewayとAWS Lambda(Node.js)を使い効率よくWebサービスと連携する その1 という記事に記載していますので、分からないかたは参照してください。
FileMakerでAPIをリクエストする
FileMakerでリクエストします。
# 変数を設定 [ $url ; 値: "https://j6z468o1r3.execute-api.us-east-2.amazonaws.com/prod" ] # 変数を設定 [ $params ; 値: blank::params ] # 変数を設定 [ $option ; 値: blank::option ] # URL から挿入 [ 選択 ; ダイアログあり: オフ ; ターゲット: blank::result ; $url ; SSL 証明書の検証 ; cURL オプション: $option ] # フィールド設定 [ blank::qrcode ; Base64Decode ( JSONGetElement ( blank::result ; "body" ) ; "tmp.png" ) ] #
無事QRコードを生成することができました。
問題点が…
しかし、現在の状態では、URLさえ分かれば、誰でもリクエストできてしまいます。
そのため、APIキーがある場合のみ実行できるようにします。
Amazon API GatewayのAPIキーを活用する
使用料プランを作成する
使用料プランを作成します。
APIステージを追加する
APIキーを作成する
「API キーを作成して使用量プランに追加」をクリックして、APIキーを作成します。
APIキーの横の「表示」をクリックして、キーを確認します。
APIキーは以下になります。
Zxuim4tqyg501E0vCz9wX2XRrzz6oiIiaoPHsVJG
API実行時にキーを必須にする
先ほど作成したPOSTメソッドをクリックします。
「メソッドリクエスト」をクリックします。
「APIキーの必要性」が false になっているので true に変更します。
デプロイする
変更したので、デプロイします。
FileMakerでAPIをリクエストして確認する
同じスクリプトを実行します。エラーになりました。
optionを以下のように修正し、スクリプトを実行すると、正常にレスポンスが取得できます。
--request POST --header "x-api-key: Zxuim4tqyg501E0vCz9wX2XRrzz6oiIiaoPHsVJG" --data-binary @$params
あとがき
今回のようにAPIを作成すれば、どのソリューションにも、数ステップをコピペするだけで、QRコードを生成することができます。
API GatewayとAWS Lambdaを活用することで、npmの様々なパッケージが使えるようになります。
今後は、以下のような記事を書きたいと思います。
- FileMaker Admin API関連
- FileMaker Data API関連
- API GatewayとAWS Lambdaを活用し、FileMaker からS3にアクセスする
- TwilioのAPIを活用し、SMSの送信やFAXの送受信をする
- EC2の自動停止や自動起動
- FileMaker Server Linuxの設定
- BaseElementsの使い方
- ターミナルの使い方や設定
- AWS CLIの使い方
リクエストがあれば、Twitterでお声かけ頂けると大変嬉しいです。
1件のコメント