Azure Functions で REST な API を作る

Azure Functions で REST っぽい WebAPI を作りたいです。

REST な API については、

にある通りで。

例えば、日本の住所を REST な API で考えると、

GET https://hoge.com/api/address/23      <-- 愛知県のデータを取得
GET https://hoge.com/api/address/23/106  <-- 愛知県/名古屋市中区のデータを取得

POST https://hoge.com/api/address/23/112 <-- 愛知県/名古屋市南区のデータを追加
PUT  https://hoge.com/api/address/23/113 <-- 愛知県/名古屋市守山区のデータを更新

DELETE https://hoge.com/api/address/13   <-- 東京都のデータを削除

という感じになります。あ、説明不足でごめん 23 は総務省が定めた 都道府県コード112市区町村コード (の下3桁)です。

このような API を Azure Functions で作りたいと思っても、通常の Http Trigger では「動的な」「多段の」パスは定義できません。

Azure Functions のプロキシを使う

そんなときに使えるのが Azure Functions のプロキシ(関数プロキシ)機能です。

関数プロキシは Http Trigger な Function の前に配置され、URL やクエリ文字列、リクエストメソッドやレスポンスのカスタマイズを可能にします。ちょうど AWS の API Gateway に相当する機能だと考えられます。

プロキシを使ってみよう

実際に REST API っぽい使い勝手を、プロキシを使って実現してみます。

ドンッ!!

image.png

Functions Apps にプロキシというグループがあるので、そこに新しいプロキシを作ります。名前 "Proxy01" は任意の名称で、これは外部に公開される URL には影響を与えません。

変数の利用

ルートテンプレートで、 {table}{*path} と付けているのが変数で、名称は任意です。

{table}/{*path} と定義すると、呼び出した URL のパス要素が、その変数に格納されます。

例えば次のようになります。

https://hoge.com/rest/address/23  ---> table='address', path='23'
https://hoge.com/rest/address/23/201  ---> table='address', path='23/201'

という感じで変数にURLパスが格納されます。定義で *path とすると、「そこまでのパス文字列すべて」が格納されます(23/201 のように)。

そしてこの変数を別な場所で使用できます。ここでは 要求のオーバーライド の項目で、クエリパラメータとして利用します。

例えば、 table : {table} という定義は、ルートテンプレートで定義した {table} 変数をクエリパラメータの table にマッピングする、という意味です。

{request.method} は組み込みの変数で、URL呼び出しのリクエストメソッド(GET/POST/PUTなど)を示します。

そして、このプロキシの透過先、実際の呼び出し先が バックエンドURL で指定されたURLで、通常は別な Http Trigger Function になると思います(このUIだけ見ると、Azure Functionsに限らず任意のURLを指定できるようですが)。

つまり、 https://hoge.com/rest/address/23/201 という URL が、別な URL https://huga.com/MyHttpTrigger?table=address&path=23/201 に変換できたとみなせます。

あとは、バックエンドURLの方でクエリ文字列をよしなに処理すれば、REST っぽい API が実装できそうです。 (https://hoge.com/api/address/23/112 のようなリストの中の要素をダイレクトに示すURLは、 path に 23/112 が入るだけなので、この文字列を自力でパースする必要はあります。)

Azure Functions の Http Trigger では次のようにクエリ文字列から table, method , path を取り出せるので、あとは SQL を組み立てるなり、DAO(Data Access Object) にお任せするなりできます。

MyHttpTrigger の index.js

module.exports = function (context, req) {
    const table = req.query.table || '';
    const method = req.query.method || '';
    const path = req.query.path || '';

    context.res = {
        headers: {
            'Content-Type': 'application/json'
        },
        body: `table=${table}, method=${method}, path=${path}`
    };
    context.done();
};