GCP Cloud FunctionsをTypeScriptで書く

Google Cloud Platform(GCP)のCloud FunctionsもTypeScriptで書きたい。そしてデプロイしたい。で、ググるとFirebaseのCloud Functionsの説明ばかり出てくるんですよね・・・FirebaseはFunction作成時にTypeScriptを選択できるみたいだけど、GCPは無理なようです。

ではどうするかというと、

  • TypeScriptのソースファイルをトランスパイルしてJavaScriptにする
  • webpackでimport,requireしているソースファイルを芋づる式に一つのJavaScriptファイルにバンドルする(まとめる)
  • ただしnode_modulesはバンドルの対象外とする

ようにします。

デプロイするファイルは、以下の通り

node_modulesの内容はバンドルしない代わりに必要なパッケージがdependenciesに書いてあるpackage.jsonをデプロイします。そうするとCloud Functionsサーバー側で必要なパッケージをダウンロードしてくれるようです。

tsconfig.jsonはこんな感じです。

{
  "compilerOptions": {
    "target": "ES2018", 
    "module": "commonjs",
    "outDir": "./dist/",    
    "strict": true,   
    "esModuleInterop": true,    
    "skipLibCheck": true, 
    "forceConsistentCasingInFileNames": true
  }
}

webpack.config.jsonはこんな感じ。

// node_modulesをバンドル対象から外すために必要
const nodeExternals = require('webpack-node-externals');
module.exports = {
   mode: "production",
   target: "node",
   externals: [nodeExternals()],
   devtool: "hidden-source-map",
   resolve: {
       modules: ["node_modules"],
       extensions: ['.ts', '.js']
   },
   module: {
       rules: [
           {
               test: /\.ts$/,
               loader: 'ts-loader'
           }
       ]
   },
   output: {
       path: __dirname + "/dist",
       filename: "index.js",
       libraryTarget: "commonjs"
   }
}

いらない設定、した方がいい設定もありそうですが・・・

Clooud Function実行の起点となる関数をmainとします。これは任意の名前で良いですがデプロイ時のオプションもその名前に合わせる必要があります。そしてmodule.exportsでこの関数を提供しておく必要があります。

function main(res, res) {
   // 諸々処理
}

module.exports = {
  main: main
}

webpackを実行するとソースコードがバンドルされたindex.jsが./dist/にできます。書いたとおりnode_modulesにあるものはバンドルしません。

$ npx webpack --entry=${起点となるts OR jsファイル}

./dist/にpackage.jsonをコピーします。これで./dist/以下にindex.jsとpackage.jsonがある状態になります。

$ cp -f ./package.json ./dist

package.jsonのmainはindex.jsにしてください。index.jsが実行の起点となるからです。

Cloud Functionsへデプロイします。

$ gcloud functions deploy ${Cloud Functionの名前} \
 --source=./dist \
 --entry-point=main \
 --runtime=nodejs12

--entry-pointで指定するのはindex.jsから最初に呼び出す関数名です。ここでは先ほど書いたとおりmainにしています。--entry-pointを省略すると呼び出される関数名はCloud Functionとしてつけた名前と同じになるはず。詳しくはdelopyのオプションを参考に。

gcloud functions deploy  |  Cloud SDK のドキュメント  |  Google Cloud