ここの issue で議論されてた新しいプログラミング モデル、情報がまとまって試せるものが出てきたのでローカル PC 環境で動かしてみた。
注意 : 2022 年 9 月現在、まだ "internal testing" phase
(パブリック プレビューですらない) で議論中なので、変更の可能性は大いにあり。
変更ポイント ~ function.json
の廃止
上述のリンク先でも Say goodbye 👋 to "function.json" files!
って言ってる。一番影響でかそう。
function.json
が廃止されると、function.json
内で設定していた
- エントリー ポイント
- トリガー / バインディング
を他で定義することになるので、簡単にその説明を。
1. エントリー ポイント
今のプログラミング モデルでは function.json
の scriptFile
フィールドで定義していた (関数の) エントリー ポイントは、(モジュール全体のエントリー ポイントとして) package.json
の main
フィールドに移動、ロードしたい js ファイルのパスを指定することに。
package.json
{ "name": "azure-functions-prototype", "version": "1.0.0", "description": "", : "main": "/PATH/TO/ENTRYPOINT.js", : }
ちなみに、この main
フィールドのパスの解析、globby
を使って独自に行っているので、ワイルド カード (**
, *
) 指定での複数ファイルの読み込みが可能。
2. トリガー / バインディング
現状 function.json
の bindings
フィールドで定義していたトリガー / バインディング設定は、新しいモデルでは上述のエントリー ポイントとして読み込まれたファイル内で、@azure\functions
からインポートできる app
にトリガーに対応するメソッド (get
, timer
, http
, storageBlob
etc) で登録していく。
以下、サンプルコード。
import { app, HttpRequest, InvocationContext} from "@azure/functions"; app.get('helloWorld1', async (context: InvocationContext, request: HttpRequest) => { context.log(`Http function processed request for url "${request.url}"`); const name = request.query.get('name') || await request.text() || 'world'; return { body: `Hello, ${name}!` }; }); app.timer('timerTrigger1', { schedule: '0 */5 * * * *', handler: (context: InvocationContext, myTimer: Timer) => { const timeStamp = new Date().toISOString(); context.log('The current time is: ', timeStamp); } });
バインディングもコードで追加する。
以下、HTTP トリガーで実行して、その時に Blob コンテナーから特定のデータを入力バインディングで取得して、その内容を出力バインディングでキューに保存する、というサンプル。
import { app, HttpRequest, HttpResponse, input, InvocationContext, output } from "@azure/functions"; const queueOutput = output.storageQueue({ queueName: 'testqueue', connection: 'storage_APPSETTING' }); const blobInput = input.storageBlob({ connection: 'storage_APPSETTING', path: 'testcontainer/testblob', }) async function helloWorldWithExtraOutputs(context: InvocationContext, request: HttpRequest): Promise<HttpResponse> { context.log(`Http function processed request for url "${request.url}"`); const name = request.query.get('name') || await request.text() || 'world'; if (process.env.storage_APPSETTING) { const message = context.extraInputs.get(blobInput); context.extraOutputs.set(queueOutput, message); } return { body: `Hello, ${name}!` }; } const extraOutputs = []; if (process.env.storage_APPSETTING) { extraOutputs.push(queueOutput); } const extraInputs = []; if (process.env.storage_APPSETTING) { extraInputs.push(blobInput); } app.http('helloWorldWithExtraInputsOutputs', { authLevel: "anonymous", methods: ['GET', 'POST'], extraOutputs, extraInputs, handler: helloWorldWithExtraOutputs });
output
や input
を @azure/functions
からインポートして、そこから取得したバインディング定義のオブジェクトを http
トリガーの関数 (handler
) と一緒に extraOutputs
, extraInputs
として登録、という流れ。
その他諸々は上述のリンクをご参照くださいませ。
プロトタイプ
このリポジトリで公開中。
手順通りにやれば特に問題な動くことが確認できる。
注意 : Azure Functions 上にデプロイしてもまだ動かない
func
コマンドの注意点
通常のインストール方法で入れた Azure Functions Core Tools の func
コマンドから直接実行しちゃいけない。
"func-cli-nodejs-v4": "4.0.4764": A preview build of the func cli that contains support for the new framework on the worker/host side of things
と書いてある通り、このプログラミング モデルでの実行には dev 版の Node.js Lauguage Worker が必要。
通常のインストール方法で入れた Azure Functions Core Tools 同梱の func
を実行してしまうと、いつも通り function.json
を探そうとして、当然見つからないので、結果「No job functions found.
」となるだけ。
上記リポジトリの場合、この dev 版 Worker は package.json
の devDependencies
にある func-cli-nodejs-v4@4.0.4764
に含まれているので、npm start
経由で func start
を実行することで、dev 版 Worker を起動できる。
感想
だいぶ変わった印象だけど、関数登録処理自体を自前のコードでやるので、その処理の前(恐らく Worker が起動してエントリー ポイントがロードされた直後の状態)にオリジナルの処理を挟みやすくなる*1。
まだ議論中の部分もありそうなので、いつ Public Preview、GA になるか、その時にはこの通り動くのかは不透明だけど、大きく変えようとしていることだけでも知っておいた方がよさそう。
他の言語も function.json 廃止の方向になるんだろうか。
*1:Top-level await はまだ使えないかも