- 投稿日:2021-12-03T22:34:29+09:00
一番スマートなNode.js+TypeScript+MySQLの使い方
はじめに 割と久々に Node.js で MySQL に繋ぐプログラムを書いてみたのですが、以前と比べて色々パワーアップしていたので、それらを組み合わせて「現時点のベストプラクティスはこんなんかな?」という感じの検討をしてみました。 多分これが一番スマートだと思います・・・という結論に達したのですが、どうだろうか Node.js はまだ触り始めたばかりなので、ご意見を頂けると幸いです ユーティリティ実装 この辺は好みが分かれるところかもしれませんが、DBアクセス用のユーティリティを作ります。 デザインパターン的にコレがベストかは少し自信がありませんが、Node.js だと流行りのパッケージはちょくちょく変わる(ex: mysql が mysql2 になったり、redisが ioredis になったり...etc)ので、自前コードで一枚噛ませて(ラップして)おいた方が良いと思います。(モノによりけりかもしれませんがインフラストラクチャ関連は特に) 関連パッケージ npm install --save dotenv npm install --save mysql2 npm install --save-dev @types/mysql DBユーティリティ実装 db.ts import mysql from 'mysql2' require('dotenv').config() class DatabaseUtility { private queryFormat: any constructor() { this.queryFormat = (query: string, values: Array<string>) => { if (!values) return query return query.replace(/\:(\w+)/g, (txt, key) => { return values.hasOwnProperty(key) ? mysql.escape(values[key]) : txt }) } } private connect(callback: (dbc: mysql.Connection) => Promise<any>): Promise<any> { return new Promise((resolve, reject) => { const dbc = mysql.createConnection({ host: process.env.RDB_HOST, user: process.env.RDB_USER, password: process.env.RDB_PASSWORD, database: process.env.RDB_NAME }) dbc.connect((error) => { if (error) { reject({ error: error }) } else { dbc.config.queryFormat = this.queryFormat callback(dbc) .then(result => resolve(result)) .catch(error => reject(error)) .finally(() => dbc.end()) } }) }) } private sendQuery(dbc: mysql.Connection, query: string, option?: any): Promise<any> { return new Promise((resolve, reject) => { dbc.query(query, option, (error, results) => { if (error) { reject({ error: error, query: query }) } else { resolve(results) } }) }) } private async sendQueries(dbc: mysql.Connection, queries: Array<{ query: string, option?: any }>) { for (var i = 0; i < queries.length; i++) { await this.sendQuery(dbc, queries[i].query, queries[i].option) } } query(query: string, option?: any): Promise<any> { return this.connect((dbc: mysql.Connection) => this.sendQuery(dbc, query, option)) } queries(queries: Array<{ query: string, option?: any }>): Promise<any> { return this.connect((dbc: mysql.Connection) => this.sendQueries(dbc, queries)) } } const db = new DatabaseUtility() export default db 上記のコードは Public Domain としておきます。 要点解説 使い方: DB とアクセスするモジュールで import db from './path/to/db' をして db.query async/await の対応処理は db.query で実装 なので、mysql2/promise ではなく mysql2 をそのまま使用 依存パッケージは少なければ少ないほど良いかなと コネクション管理とか面倒なので上位レイヤーには意識させない db.query の都度 connect ~ end している db.queries で 1 回の connect ~ end で複数クエリ実行できるようにしている 当初 ConnectionPool を使って db.query だけで良いかと思ったが、AWS Aurora での使用を想定するとフェイルオーバー時に事故りそうだったので都度接続方式にした SQL エラー時は { query: query, error: error } でエラーになった query とエラーオブジェクトを返す ...ようにしたのですが開発時に出てくる dbc.query のエラーケースの大半は SQL の構文エラーなので reject(new Error(`SQL error: ${query}`)) とかにしちゃって良いかも? テスト 準備 .env ファイル RDB_HOST="localhost" RDB_USER="root" RDB_PASSWORD="password" RDB_NAME="test1" SQL CREATE DATABASE test1; CREATE TABLE test1.table1 ( id BIGINT AUTO_INCREMENT, name TEXT, value INT, KEY(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; コード test.ts import db from './db' (async () => { const item1 = { name: "hoge", value: 1234 } const item2 = { name: "hige", value: 5678 } const item3 = { name: "huga", value: 90 } const sql = "insert into table1 (name, value) values (:name, :value)" await db.query(sql, item1) await db.query(sql, item2) await db.query(sql, item3) return await db.query("select * from table1") })().then((result) => { console.dir(result) }).catch((error) => { console.error(error) }) 実行結果 正常にクエリ select * from table1 の実行結果が表示されました。 % npx ts-node test [ { id: 1, name: 'hoge', value: 1234 }, { id: 2, name: 'hige', value: 5678 }, { id: 3, name: 'huga', value: 90 } ] まとめて実行したい場合 ConnectionPool を使っておけばこれの存在意義はなかったのですが、AWS Aurora でのユースケースを想定して都度 Connect する方式に変更しているので、複数クエリをまとめて実行したい場合、以下のようにすれば高速になります。 await db.queries([ { query: sql, option: item1 }, { query: sql, option: item2 }, { query: sql, option: item3 }, ]) まとめ async/await いいですね
- 投稿日:2021-12-03T17:53:20+09:00
# nvmでNode.jsを(Ubuntu/Windows)にインストールする方法
はじめに https://github.com/nvm-sh/nvmを見ればまず間違いない. この記事でわかること nvmを使ったNode.jsのインストール方法(Ubuntu/Windows) Nodeのバージョン確認や切り替え等のコマンド(nvmコマンドはどのOSでも共通) nvmのインストール方法 Node Version Manager(NVM)とはその名の通り,Nodeのバージョンを管理してくれるソフトウェア.使用するnpmのパッケージによっては異なるバージョンを求められることがあるため,nvmのようなバージョン管理ツールは使って損しない. Ubuntu 公式の README に従って進める. curlが入っていなければインストールする. curlはコマンドラインツールの一種で,URLの先にある場所とデータの送受信を行うために使われる.以下のコマンドを端末画面で入力してインストール. sudo apt install curl curlをインストールしたら以下のコマンドを端末画面で入力してnvmをインストールする. curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash これでインストール完了. Windows nvm-windowsを利用してインストールする. こちらのページ (https://github.com/coreybutler/nvm-windows/releases/) にアクセスして,ページ中程のnvm-setup.zipをダウンロード. 解凍したフォルダ内のnvm-setup.exeを実行し,画面の指示に従ってセットアップを完了させる. コマンドプロンプトかWindows PowerShellを立ち上げてnvmコマンドを実行したときにバージョン等が表示されればOK. Node.jsのインストールとnvmコマンド 先程インストールしたnvmを使ってNodeをインストールする. nvmコマンドはどのOSでも同じものを使用できる. これも公式の README にUsageがあるので,それを見ると捗る. node_versionの所は各自入れたいバージョンの数字に読み替える. 例えば,v14とか,v14.7.0とか. また,小数点以下を指定しない場合はバージョンの内で最も新しいものがインストールされる. nvm install node_verson ちなみに nvm ls-remote で現在インストール可能な全てのバージョンを確認出来る. 使用するnodeのバージョンを切り替えるにはnvm useを使う. nvm use node_version 現在使用しているnodeの確認とインストール済みnodeを表示するにはnvm ls nvm currentだと現在nvm useしているバージョンのみが表示される. いらないバージョンのnodeをアンインストールするには以下のコマンドを使用する. nvm uninstall node_version 以上.
- 投稿日:2021-12-03T15:30:58+09:00
Lisk SDKでブロックチェーンアプリケーションを作る
はじめに Liskって名前を知っていてもLisk SDKに触れたことがある人は少ないのでは、ということで今回記事にしました。 丁度いいところに、アドベントカレンダーもあったしね。 構築手順を書いているので少し長いですがお付き合いください。 Lisk SDK ってなんぞ ブロックチェーンとその上で動作するアプリケーション(ブロックチェーンアプリケーション)を構築するための開発キット。 これを使用して作成されたアプリケーションは独自のブロックチェーン上で動作します。 また、将来的にサイドチェーンとしてLiskにつながることが可能になります。 なお、JavaScript、TypeScriptで構築できるので非常に楽。(いや、ホントに) サイドチェーンとしてLiskと繋ぐ部分(インターオペラビリティ周り)はまだ開発中 サイドチェーンとなる必要がないなら、今のままでも新プロジェクトとして立ち上げられるくらいの実力有り 推奨構築環境 OS ubuntu 18.04 (LTS) 20.04 (LTS) mac 10.13 (High Sierra) 10.14 (Mojave) 10.15 (Catalina) 11.04 (Big Sur) Windowsは残念ながら... RAM 2GB以上 Node.js v12.22.7 (NPM version 6.14.15) v12系以外を使用するとうまく動作しません 構築方法 1. 下準備 とりあえずこの辺いれとく sudo apt-get install curl wget tar unzip zip ntp jq git build-essential 2. Lisk Commander の導入 npm i -g lisk-commander とても便利なCLI 入れなくても構築できるが、入れておいて損はなし 今回の記事では入れている前提 3. ブロックチェーンアプリケーションのプロジェクトを作成 lisk init omikuji lisk init [アプリケーション名] 4. 作成したプロジェクトを開く 以降 Visual Studio Codeなどを使って作業するのをおすすめ 5. 便利なプラグインの追加 以下のファイルを編集 /src/app/plugin.ts import { Application, HTTPAPIPlugin } from 'lisk-sdk'; // add HTTPAPIPlugin import { DashboardPlugin } from '@liskhq/lisk-framework-dashboard-plugin'; // add DashboardPlugin // @ts-expect-error Unused variable error happens here until at least one module is registered export const registerPlugins = (app: Application): void => { app.registerPlugin(HTTPAPIPlugin); // register HTTPAPIPlugin app.registerPlugin(DashboardPlugin); // register DashboardPlugin }; HTTPAPIPlugin:ブラウザ等でAPIをたたきたいときに必須 DashboardPlugin:構築したアプリケーションの動作確認に使用できるので開発中は入れておくと便利 6. モジュールとアセットの作成 Lisk SDKではモジュールとアセットで1つのトランザクションとして扱われます。 Lisk標準のモジュール・アセット(一部抜粋) moduleID assetID name 2 0 token:transfer 5 0 dpos:register 5 1 dpos:vote 5 2 dpos:unlock 今回はおみくじを作りたいので、以下のような感じにします。 moduleID assetID name 3535 0 omikuji:pull ということで lisk generate:module omikuji 3535 lisk generate:asset omikuji pull 0 lisk generate:module [モジュール名] [モジュールID] lisk generate:asset [モジュール名] [アセット名] [アセットID] ここまでやると以下のファイルが作成されます。 /src/app/modules/omikuji/omikuji_module.ts /* eslint-disable class-methods-use-this */ import { AfterBlockApplyContext, AfterGenesisBlockApplyContext, BaseModule, BeforeBlockApplyContext, TransactionApplyContext } from 'lisk-sdk'; import { PullAsset } from "./assets/pull_asset"; export class OmikujiModule extends BaseModule { ...... public name = 'omikuji'; public transactionAssets = [new PullAsset()]; public events = [ // Example below // 'omikuji:newBlock', ]; public id = 3535; ...... public async afterGenesisBlockApply(_input: AfterGenesisBlockApplyContext) { // Get any data from genesis block, for example get all genesis accounts // const genesisAccounts = genesisBlock.header.asset.accounts; } } /src/app/modules/omikuji/asset/pull_asset.ts import { BaseAsset, ApplyAssetContext, ValidateAssetContext } from 'lisk-sdk'; export class PullAsset extends BaseAsset { public name = 'pull'; public id = 0; // Define schema for asset public schema = { $id: 'omikuji/pull-asset', title: 'PullAsset transaction asset for omikuji module', type: 'object', required: [], properties: {}, }; public validate({ asset }: ValidateAssetContext<{}>): void { // Validate your asset } // eslint-disable-next-line @typescript-eslint/require-await public async apply({ asset, transaction, stateStore }: ApplyAssetContext<{}>): Promise<void> { throw new Error('Asset "pull" apply hook is not implemented.'); } } また、以下のファイルに作成されたモジュールが自動で追加されます。 /src/app/modules.ts import { Application } from 'lisk-sdk'; import { OmikujiModule } from "./modules/omikuji/omikuji_module"; // add // @ts-expect-error Unused variable error happens here until at least one module is registered export const registerModules = (app: Application): void => { app.registerModule(OmikujiModule); // add }; 7. モジュールとアセットの編集 あとは自由にどんなアプリケーションにしたいか考えながら作成されたファイルを編集していきます。 今回のおみくじは以下のような仕様 1LSK以上つかってお願いすると「大吉、吉、中吉、小吉、末吉、凶、大凶」を教えてくれる 1LSK未満だと神様に怒られる おみくじの結果はアカウント情報のomikuji.kekkaに設定される おみくじを引く際のパラメータは {"onegai": 数値} /src/app/modules/omikuji/omikuji_module.ts /* eslint-disable class-methods-use-this */ import { AfterBlockApplyContext, AfterGenesisBlockApplyContext, BaseModule, BeforeBlockApplyContext, TransactionApplyContext } from 'lisk-sdk'; import { PullAsset } from "./assets/pull_asset"; export class OmikujiModule extends BaseModule { public actions = {}; public reducers = {}; public name = 'omikuji'; public transactionAssets = [new PullAsset()]; public events = []; public id = 3535; // アカウント情報に追加するおみくじの結果を設定するためのスキーマの設定 public accountSchema = { type: 'object', properties: { kekka: { fieldNumber: 1, dataType: 'string', maxLength: 64, }, }, default: { kekka: '', }, }; // Lifecycle hooks public async beforeBlockApply(_input: BeforeBlockApplyContext) {} public async afterBlockApply(_input: AfterBlockApplyContext) {} public async beforeTransactionApply(_input: TransactionApplyContext) {} public async afterTransactionApply(_input: TransactionApplyContext) {} public async afterGenesisBlockApply(_input: AfterGenesisBlockApplyContext) {} } /src/app/modules/omikuji/asset/pull_asset.ts import { BaseAsset, ApplyAssetContext, ValidateAssetContext, cryptography } from 'lisk-sdk'; export class PullAsset extends BaseAsset { public name = 'pull'; public id = 0; // トランザクション実行時のパラメータ用のスキーマを設定 public schema = { $id: 'omikuji/pull-asset', title: 'PullAsset transaction asset for omikuji module', type: 'object', required: ["onegai"], properties: { onegai: { dataType: 'uint64', fieldNumber: 1, }, }, }; public validate({ asset }: ValidateAssetContext<{onegai:BigInt}>): void { // 1LSK未満なら怒る if (asset.onegai < BigInt(100000000)) { throw new Error( '1LSKくらいはらうのじゃー' ); } } // eslint-disable-next-line @typescript-eslint/require-await public async apply({ asset, transaction, reducerHandler, stateStore }: ApplyAssetContext<{onegai:BigInt}>): Promise<void> { // おみくじ const kuji = ["大吉","吉","中吉","小吉", "末吉", "凶", "大凶"]; const kekka = kuji[parseInt(cryptography.bufferToHex(transaction.id).slice(0, 8), 16) % kuji.length]; // 送信者の情報に結果を設定 const senderAddress = transaction.senderAddress; const senderAccount:{address: Buffer, omikuji:{kekka:string}} = await stateStore.account.get(senderAddress); senderAccount.omikuji.kekka = kekka; stateStore.account.set(senderAccount.address, senderAccount); // 送信者の残高からお願いに使ったLSKを減らす await reducerHandler.invoke("token:debit", { address: senderAddress, amount: asset.onegai, }); } } 8. Genesis Blockの作成準備 ./bin/run genesis-block:create --output config/default A genesis_block file already exists at the given location. Do you want to overwrite it?と聞かれたらy すると、config/defaultに以下のファイルが生成されます accounts.json forging_info.json genesis_block.json password.json config/default内の各JSONファイル(config.jsonを含む)はGitHubなどで公開する際は十分気をつけましょう。 ここで生成されるファイルを公開し、そのままアプリをリリースしようものなら大変なことになります。 絶対にアカウントのパスフレーズや、パスワードは公開してはいけません。 forging_info.jsonおよびpassword.jsonの内容をconfig.jsonに反映 tmp=$(mktemp) jq '.forging.delegates = input' config/default/config.json config/default/forging_info.json > "$tmp" && mv "$tmp" config/default/config.json jq '.forging += input' config/default/config.json config/default/password.json > "$tmp" && mv "$tmp" config/default/config.json 9. ブロックチェーンアプリケーションの起動 ./bin/run start --api-ws 10. 動作確認 ブラウザから http://localhost:4005 にアクセス Send transactionから作成したモジュール・アセットを選択して「Submit」 動作確認の際は accounts.json に記載されているものを使用すると楽 なお、1LSK = 100,000,000 成功するとこんな感じ onegai を 1LSK未満にすると怒られる HttpAPIで成功した際のトランザクションIDを確認してみる HttpAPIでおみくじの結果(送信者のアカウント情報)を確認してみる 「大凶」だったみたいです... ということで SDKの機能の1割も使ってないんじゃないかというくらい簡単なアプリでしたがなんとなくイメージは掴めたでしょうか? 必要スペックも低い、ブロックチェーン構築も楽、アプリケーションの実装も楽 言うことなしだと思いません!? ぜひ触ってみてくださいね! なお、開発で困ったことがあれば Lisk 公式Discord や、Lisk Dev forum で尋ねるとコミュニティの誰かがきっと助けてくれます! また、ブロックチェーンアプリケーションを本格的なプロジェクトとして構築していくのであればGrant Programに応募してみてはどうでしょうか? 読んでいただきありがとうございました! お疲れ様でした! 補足:ブロックチェーンアプリケーションの削除 rm -rf ~/.lisk/[アプリケーション名] データのみ消す場合は rm -rf ~/.lisk/[アプリケーション名]/data
- 投稿日:2021-12-03T13:08:48+09:00
Nodejs の postgresql ドライバ (Sequelize, Prisma) を少し触ってみた。
@kenmaroです。 普段は主に秘密計算、準同型暗号などの記事について投稿しています。 秘密計算に関連するまとめの記事に関しては以下をご覧ください。 秘密計算エンジニアを始めて1年が経った。 秘密計算エンジニアを始めて2年半が経った。 概要 Nodejs のドライバを使ってpostgresqlに接続するテストを行った際に、 どのようなドライバやライブラリ、フレームワークがあるのかを調べたので簡単に備忘録的にまとめてみます。 初めにまとめ いくつかDBドライバ的なものは存在して、有名なものも幾つかありましたが、 この二つが中でも有名なようでした。 Sequelize DBへのORMをもったライブラリであり、簡単に接続したいのであればこれを使うのが良さそうです。 Prisma ライブラリというよりはフレームワーク的な感じでいろいろとフォルダ構成など必要になるようです。 ウェブアプリなどを構成する上でがっつりと開発するなら使ってみてもいいでしょう。 かなり人気が高まっているようです。 どちらも試してみたのですが、 私の場合目的がサクッと試すという目的だったため、Sequelize を最終的には主に使いました。 環境構築 postgresql はローカルに構築せず(してもいいですが、)、Docker を使ってサクッと開発環境を構築した方が簡単です。 docker-compose.yml version: "2" services: mysql: restart: always image: mysql:latest ports: ["3306:3306"] environment: MYSQL_DATABASE: 'test' MYSQL_ROOT_PASSWORD: 'password' MYSQL_USER: 'postgres' MYSQL_PASS: 'password' volumes: - /mnt/data/mysql:/var/lib/mysql pgdb: container_name: pg_db image: postgres:latest ports: ["5432:5432"] environment: - POSTGRES_PASSWORD=password" command: ["postgres", "-c", "log_statement=all", "-c", "log_destination=stderr"] このように mysql やpostgresql を簡単に構築できるdocker-compose.yml を 作っておくととても便利ですね。 このymlと同階層で、 $docker-compose up -d pgdb で開発postgresqlを立ち上げましょう。 立ち上げたら、psqlというクライアントCLIライブラリを使って接続テストしてみます。 デフォルトでは postgres というユーザ と postgres というデータベース が構築されており、 パスワードはdocker-compose で指定しているものです。 よって、接続コマンドは psql -U postgres -h localhost -d postgres となります。 このあと、自分で開発用のユーザを作ったり、開発用のデータベースを作ってもいいですし、 めんどくさければとりあえずデフォルトのユーザpostgres、データベースpostgres を使ってもいいと思います。 psqlでいろいろDBをいじるコマンドは以前まとめたのでこの記事をご覧ください。 Sequelize 公式はこちらから 導入には以下を参考にさせていただきました。 コネクション確立のパラメターなどはこちらを参考に https://sequelize.org/master/class/lib/sequelize.js~Sequelize.html#instance-constructor-constructor 実際にコネクトしてみます。 フォルダ構成などは関係なく、index.jsだけで実質大丈夫なのでサクッと試せます。 mkdir nodetest cd nodetest npm init -y npm install --save sequelize npm install --save pg pg-hstore touch index.js index.js const { Sequelize, Model, DataTypes, QueryTypes } = require('sequelize'); //const sequelize = new Sequelize('sqlite::memory:'); const sequelize = new Sequelize('postgres', 'user', 'password', { host: 'localhost', port: 8088, dialect: 'postgres', pool: { max: 5, min: 0, acquire: 30000, idle: 10000 } }); class Test extends Model {} Test.init({ c1: DataTypes.STRING, }, { sequelize, modelName: 'test' }); (async () => { await sequelize.sync(); //const res = await Test.findAll(); //const res = await sequelize.query(`SELECT * FROM "test"`, { type: QueryTypes.SELECT }); const res = await sequelize.query('SELECT id FROM "test"', { type: QueryTypes.SELECT }); console.log("here done"); console.log(res); //const jane = await Test.create({ // username: 'janedoe', //}); //console.log(jane.toJSON()); })(); node index.js これでクエリ結果を受け取ることができるはずです。 Prisma Prisma についてはまだそこまで記事が多くないイメージでしたが、 私は上二つの記事をフォローして触ってみるのが一番しっくりきました。 しかしながら、ちょっとサクッと試すにはステップが多いかなあと思いました。。 まとめ 最近は個人的にはfirebase をひたすら使っており、久しぶりにnodejsのライブラリについて調べてみましたが、 なかなか面白かったです。 他の言語のドライバについても調べてみようかな、、 今回はこの辺で。 @kenmaro
- 投稿日:2021-12-03T12:44:17+09:00
DockerコンテナにDockerfileの変更が反映されない
はじめに 現在、Web系自社開発企業への転職を目標にポートフォリオを作成中。 Dockerを活用してLEMP環境を構築し、Laravel+VuejsでWebアプリケーションの開発を行っております。 Dockerコンテナのnode.jsのバージョンを変更させるために、Dockerfileを編集した際に Dockerfileの変更がコンテナに反映されないという問題で少しつまづいたので、記録として残します。 目次 やりたいこと やったこと 原因 解説 解決策 参考文献 やりたいこと Dockerfileを編集して、DockerコンテナのNode.jsのバージョンを変更したい やったこと Dockerfileを編集 変更前 Dockerfile # install Node.js COPY --from=node:10.22 /usr/local/bin /usr/local/bin COPY --from=node:10.22 /usr/local/lib /usr/local/lib 変更後 Dockerfile # install Node.js COPY --from=node:16.13 /usr/local/bin /usr/local/bin COPY --from=node:16.13 /usr/local/lib /usr/local/lib コンテナの停止 docker compose stop コンテナの作成・起動 docker compose up -d Node.jsのバージョンを確認 docker compose exec app node -v v10.22.0 Dockerfileの変更が反映されなかった 原因 Dockerfileを変更する前のイメージを参考にしてDockerコンテナを作成、起動したから 解説 DockerコンテナはDockerイメージを参考にして作成される そして、このDockerイメージはDockerfileを参考にして作成されるため Dockerfileの変更をDockerコンテナに反映させるためには、Dockerイメージの作成から実行する必要がある 解決策 buildしてDockerイメージを作成 docker compose build Dockerコンテナを作成、起動 docker compose up -d Node.jsのバージョンを確認 docker compose exec app node -v v16.13.0 無事、Dockerfileの変更がコンテナに反映された 参考文献 Docker利用時のソースコードの変更反映 Dockerfileの変更が反映されない
- 投稿日:2021-12-03T09:13:31+09:00
CloudFunctionsアプリをDevContainerでさくっと開発する
クラウドコンピューティング、とりわけサーバーレス環境は 周りのめんどくさい事に煩わされずに「提供するモノの中身に注力できる」という点でより素早く、品質の良いサービスを提供できるシカケでもあります。 本稿では、開発環境でも「より中身に注力」するためにさくっと環境を立ち上げてデプロイする事について触れようと思います。 TL;DR VisualStudioCodeのDevContainerでコンテナ内に開発環境を作る ローカルでCloudFunctionsを実行する 開発環境からCloudへデプロイする 想定環境 Windows10 GoogleCloud(CloudFunctions) node.js VisualStudioCode ローカルマシンにインストールするのはふたつ クラウドのアプリケーション開発に携わる方にとって、同時に複数のサービスを開発したりメンテナンスする必要に迫られる事はありませんか? それぞれのアプリケーションが違うバージョンのフレームワークやプラグインを使っていたり、そもそも稼働するクラウドやリポジトリが違ったりしてそれらの切り替え(アタマの切り替えも含めて)が煩わしかったり間違えたりすることは出来るだけ避けたいものです。 その為に、 個々の開発環境はコンテナの中に閉じ込めましょう。 ローカルマシンにインストールするのは、 ・ DockerDesktop ・ VisualStudioCode+DevContainer拡張機能 の、二つだけです。 node.jsを使ったCloudFunctions開発環境をさくっと作ります node.js開発コンテナを作ります 今回はWindowsでの操作を例示しますが、Macではキーやコマンドがやや違います。 ローカルマシンに開発用フォルダを作ってそこに移動します。 > mkdir myFirstFunction > cd myFirstFunction そこでVisualStudioCode(以下VSCode)を立ち上げます。 > code VSCodeが立ち上がったらコンテナの設定をします。 Shift+Ctrl+Pでメニューを開いて、open containerと入力して、フィルタされた選択肢の中から「Open Folder in Container...」を選択、Enterを押します。 今回はnode.js 14のコンテナで開発しましょう。 あとはデフォルトでOKです。 ネットワーク帯域等の条件により異なりますが、数分程度でコンテナの作成が終わると思います。 そうすると、自動でターミナルが表示されます。 さぁ、開発を始めます まず、node.jsを初期化しましょう。 ここからはVSCode(DevContainer)のターミナルでの作業になります。 node ➜ /workspaces/myFirstFunction $ npm init いくつかプロンプトで聞かれますが、特にこだわりがなければすべてデフォルト(Enter)で構いません。 package.jsonが出来上がりました。 package.json { "name": "myfirstfunction", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" } それではアプリケーションを作りこみます。 まずはシンプルなWebサービスです。 index.jsファイルを作りましょう。 index.js exports.myFirstFunction = (req, res) => { const name = req.query.name || req.body.name || 'World'; res.send(`Hello, ${name}!`); } ローカルで動作確認します これをローカルで実行するために、 @google-cloud/functions-frameworkをインストールします。 $ npm install --save-dev @google-cloud/functions-framework package.json のdevDependenciesに追加されていることが分かります。 package.json(抜粋) ... "license": "ISC", "devDependencies": { "@google-cloud/functions-framework": "^2.0.0" } } では、実行してみましょう。 $ npx functions-framework --target=myFirstFunction --signature-type=http こんな画面が現れますので、[OpenInBrowser]を押してやると... ブラウザに表示されました。 ポートの自動フォワードについてはターミナルの隣のタブ「PORTS」で確認できます。 GoogleCloudへデプロイしましょう gcloudツールをインストールします デプロイの為にはgcloudコマンドを使います。 これも簡単にインストールします。 $ curl https://sdk.cloud.google.com | bash プロンプトへはすべてデフォルト(Enterのみ)でOKです。 インストールしたgcloudコマンドを有効にするために新しいターミナル画面を開きます。 gcloudを設定します。 $ gcloud init プロンプト You must log in to continue. Would you like to log in (Y/n)? にY (デフォルト)で答えます。 すると、以下の様に認証画面へのurlが表示されますので、ctrl+click Go to the following link in your browser: https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=3...(略) Enter verification code: googleの認証画面が表示されますので、gcloudへアクセスできるアカウントで認証します。 GoogleCloudSDKへの許可をします。 そこで認証キーが表示されるので、コピーボタンでコピーして、 VSCodeのプロンプトへペーストしてEnterを押します。 Enter verification code: 4/1AX...(略) 続いてデプロイ先のプロジェクトを選んだらOK。 GoogleCloud側でビルドAPIを有効にします GCPコンソールのナビゲーションメニュー→APIとサービス→ライブラリで、APIライブラリ画面が開きます。 APIとサービスを検索の窓に、cloud build apiと入力して、恐らく1番目に表示される「Cloud Build API」をクリック、 表示された画面で「有効にする」をクリックします。 いよいよデプロイ 対象のプロジェクトへ先程作ったアプリをデプロイします。 $ gcloud beta functions deploy myFunction \ --runtime=nodejs14 \ --entry-point=myFirstFunction \ --trigger-http \ --allow-unauthenticated 何か聞かれたらデフォルト(Enter)でOKです。 動作確認しましょう ナビゲーションメニュー→CloudFunctionsを選びます。 表示されていますね。 このmyFunctionをクリック、トリガータブを選ぶとURLが見えますのでクリックしてみましょう。 出ました! おしまい ここでご案内した手順についてはもちろん本家サイトに詳しく記載されています。 本稿ではチートシート的に必要な手順を抜き出してみました。 あなたのプロジェクトで、より「中身に注力」するためにご参考になれば嬉しいです。
- 投稿日:2021-12-03T02:00:18+09:00
ServerlessFrameworkでDynamoDBLocalを使う
今更ですが、Serverless FrameworkでDynamoDBのローカル開発環境を構築するために、DynamoDB Localを導入したので、その記録です。 はじめに DynamoDB Local 自体は、そもそも aws 公式のツールです。 DynamoDB をローカル環境で利用できるように用意されています。 JRE 上で動作するので、Java6以上の実行環境が必要です。1 それを Serverless Framework で使うための Serverless DynamoDB Local というプラグインがあります。 プラグインを利用しなくても DynamoDB Localを利用することはできますが、serverless frameworkの設定と一緒にかけたり、seedなどのオプションがあり便利です。 Serverless Framework で DynamoDB Local の導入手順 プラグインをインストールします。 # Serverles DynamoDB local pluginの依存モジュールのインストール npm install -D serverless-dynamodb-local 次に、serverless.yml に serverless-dynamodb-local の plugin の指定を入れます。 serverless-webpack や serverless-offlineのプラグインを一緒に利用するときは、以下の順番で指定します。 plugins: - serverless-webpack - serverless-dynamodb-local - serverless-offline Serverles DynamoDB local プラグイン のインストール後に、DynamoDB Local 本体をインストールします。 プラグインをインストールすると、以下のコマンドで DynamoDB Local をインストールできます。 # dynamodb local のインストール sls dynamodb install インストールが完了すると、 .dynamodb ディレクトリが作成され、jarなどが配置されます。 sls dynamodb start のコマンドで、DynamoDB Localが起動します。 あとは 以下で設定するポートに対してアクセスして(例: https://localhost:8000)利用するかんじになります。ソースコードからや、awscliからの利用方法は後述します。 DynamoDB Local の設定 inMemoryで起動したり、初期データ作成をするためのオプションが提供されています。 以下のように serverless.yml の custom での設定も可能で、公式にオプションの説明があるので、細かく設定したい時に見ると良さそうです。 custom: dynamodb: stages: - dev start: port: 8000 inMemory: true migrate: true seed: true convertEmptyValues: true serverless-offlineと起動 Serverless Framework のローカル開発環境では、よく serverless-offline を利用していると思います。 serverless-offlineと一緒に利用していれば、 sls offline で DynamoDB Local も一緒に起動してくれます。2 serverless-webpackも使っている場合は、sls offline start で起動する必要があります。 逆にDynamo Localを個別に起動しておきたい場合 は、 上述の serverless.yml のcutom 設定で、 noStart: true を設定することで、 sls offlineで Dynamo Local が一緒に起動しなくなります。 ソースコードからの利用方法 ローカル開発時だけ、DynamoDB Local に向けて、AWSにプロビジョニングしたときは本物のDynamoDBに接続したいときは、ソースコード上で向き先を変更する必要があります。 これは、Serverless Frameworkのサンプルコードなどを参考にするといいでしょう。 オフラインかどうかの判定は、sls offline を起動すると、IS_OFFLINEの環境変数が設定されるので、それをみて接続先を切り替えます。 AWS CLI でのコマンド実行 ローカル開発中に、DynamoDB Localに入っているデータを閲覧、編集、削除などしたい場合があると思いますが、awscliでエンドポイントを指定すれば簡単に操作することができます。 # table一覧 aws dynamodb list-tables --endpoint-url http://localhost:8000 # 指定したテーブルをscan aws dynamodb scan --table-name <table名> --endpoint-url http://localhost:8000 また、ブラウザ上で操作ができる「JavaScript console」なるもの用意されていて、以下のようにリンクが表示されるんですが、自分の環境ではアクセスしても表示されなかったです。(原因がわかったら追記します。) $ sls dynamodb start Dynamodb Local Started, Visit: http://localhost:8000/shell その他 上記はDynamoDB Localの設定方法の説明なだけで、DynamoDBのテーブル定義は、別途 Resouces で設定する必要があります。CloudFormationの構文で書いていくだけです。 serverless.yml や serverless.ts の設定は、npx sls print で正しいかチェックできるので利用すると良いでしょう。 Docker イメージもあるようで、docker でも動作させることができます。 ↩ serverless.yml での serverless-offline プラグインの指定を最後に書く必要があります。 ↩