#Overview
フロントエンドはReact、バックエンドはNode.jsで作っていると、どちらでも利用する処理はソースコードを共有したくなります。
別の記事でesmというパッケージを知り、Google Cloud Functions(以降、GCF)でもimport/export構文を使いたくなりました。
GCFのフレームワーク内で動くから厳しいかな?と思っていたのですが、至って簡単だったので残しておきます。
#Target reader
- JavaScriptとNode.jsでソースコードを共有したい方。
#Prerequisite
- バックエンドはGCFを利用する、つまりNodeの起動オプションを指定するようなことはできない。
- Node.jsのバージョンはGoogle Cloud Function(GCF)に依存し、現時点ではV10系とする。
#Body
##どうしてimport/export構文にするの?
私のPJの場合、バックエンドで作成した機能をフロントエンドで実行するかも?という機能がいくつかあります。
そうなるとどちらも同じJSだし、フロントとバックを意識する必要性をできたらなくしたいよねという欲求にかられます。
フロントからバックへ移動、バックからフロントへ移動、もちろんパッケージとして切り出すことも可能とソース変更なしに自由に扱えます。
(もちろんブラウザ、もしくはNode.js固有のAPIを使うとそこに対しての手当ては必要になります)
そのような理由からimport/exportに寄せることにしました。
パッケージで共有する場合については以下の記事を参考にしてください。
https://qiita.com/qrusadorz/items/cc2e76afa1d34f1a980c
##esmとは
Node.jsではV12系まで起動時にフラグを付けないとimport/exrpot
構文が使用できないようです。
しかし、esmパッケージを利用するとV10系でもimport/exrpot
構文が利用可能になります。
更に、npm init
コマンドに組み込まれているほどのものなので安心できるでしょう。
esmを使用しないnpm init
をlegacy initといっているのが象徴的です。
https://docs.npmjs.com/cli/init
もう少しesmについて知りたい方は、作者の方が投稿された記事を見てみるといいかもしれません。(わたしも詳しく理解していない)
https://medium.com/web-on-the-edge/tomorrows-es-modules-today-c53d29ac448c
##GCFのソースコードを眺める
https://cloud.google.com/functions/docs/first-nodejs?hl=ja
https://firebase.google.com/docs/functions/http-events
exports.helloHttp = (req, res) => {
res.send(`Hello ${escapeHtml(req.query.name || req.body.name || 'World')}!`);
};
GCF固有のルールで関数を外に出してるかと思いきや、これはNode.jsのexports構文そのものじゃない?と気が付く。
私はmodule.exports
の方しか使っていなかったので気が付くのが遅れてしまった...
It allows a shortcut, so that module.exports.f = ... can be written more succinctly as exports.f = ....
Google翻訳先生曰く
module.exports.f = ...をexports.f = ...としてより簡潔に記述できるように、ショートカットを許可します。
つまり、module.exports
使っているだけで特別なことは何もないことが判明。
##esmを導入する方法
これはGitHubにしっかり書かれている。
https://github.com/standard-things/esm
Enable esm for local runs:
node -r esm main.js
一つ目は起動時にnodeコマンドにesmをプリロードする方法。
-r
は後続のモジュールをプリロードするオプション。
https://nodejs.org/dist/latest/docs/api/cli.html#cli_r_require_module
この方法はGCFではnodeコマンドに介入できないことから使用できない。
二つ目はesmを介してrequire
する方法。
Use esm to load the main ES module and export it as CommonJS.
index.js
// Set options as a parameter, environment variable, or rc file.
require = require("esm")(module/*, options*/)
module.exports = require("./main.js")
main.js
// ESM syntax is supported.
export {}
npm init esm
を実行すると、エントリーポイントのindex.jsにesmのラッパーが入り、main.jsに自身のコードを記述するようになる。
前項のGCFが標準のmodule.exports
を用いていることを考えると、通常のnpm init esm
と同じように作ればFunctionsで動きそうという予測ができる。
##npm init esmでGCFの関数を作る
新規のフォルダでnpm init esm
を実行する。
npm init esm
適当にEnterキーなり文字入力してpackage.jsonが作成される。
この時すでにindex.jsにesmの仕込みができているのが確認できる。
公式ドキュメントではindex.jsに関数をつくるが、ここではmain.jsにimport/export
構文で記述する。
処理については、n-gramというパッケージを利用し、hogeという文字列を2文字ずつに切り出す処理に変更する。
https://cloud.google.com/functions/docs/first-nodejs?hl=ja#creating_a_function
import nGram from 'n-gram';
const helloHttp = (req, res) => {
const text = "hoge";
const bigram = nGram.bigram(text) || [];
res.send(`Hello!` + bigram);
};
// ESM syntax is supported.
export {
helloHttp
}
デプロイ前にパッケージをインストール。
npm i n-gram
公式ドキュメントに沿ってデプロイする。
gcloud functions deploy helloHttp --runtime nodejs10 --trigger-http
コンソールから実行すると、n-gramをインポートして、hogeという文字列を2文字ごとに分割できているのが確認できる。
$ Hello!ho,og,ge
ちなみに以下のようにrequireとmodule.exportsを使った従来の形式でも問題なく動く。
const nGram = require('n-gram');
const helloHttp = (req, res) => {
const text = "hoge";
const bigram = nGram.bigram(text) || [];
res.send(`Hello!` + bigram);
};
exports.helloHttp = helloHttp;
// or
// module.exports = {
// helloHttp
// }
##esmを導入に関する注意点
nodeコマンドで実行する場合、GCFで実行する場合のどちらでも問題ないことを示した。
これでもはやrequireを利用する必要がなくなったと思っていたが盲点が一つ。
jestではesmを意識しないで利用できるような方法がまだない。
テストフレームワークであるjestでスマートにサポートされていないのが痛い。
ただし、テストコードの中でesmを介したrequireを使えばいい模様。
https://github.com/standard-things/esm/issues/97#issuecomment-492435481
また、esmもjestもこれについては対応するために動き出しているので、時間がいずれ解決するものと思われる。
https://github.com/standard-things/esm/issues/706
https://github.com/facebook/jest/issues/9430
よって、現時点ではesmをテストコードで意識する必要があるが、これを理由に導入を見送るほどの理由にはなりえない。
#Conclusion
今回はGCFで利用しましたが、exports
を利用していることからAWSのLambdaやAzure Functionsでも同様に利用できると思われます。
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/nodejs-prog-model-handler.html
https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-reference-node
esmもjestの件があるため完璧ではないですが、import/export構文で統一できる状況が整いつつあるといった印象です。
import/exportで統一する未来を先取りして動きたい方にはesmは最良のパッケージだと思います。
もちろん導入後にそれまでのテストコードを通すことは必須です。(私もesmを本格導入するのはこれからなので)
Have a great day!
#Appendices
なし
#References
本体に全て掲載