LoginSignup
19
18

More than 5 years have passed since last update.

Azure Functions × Cosmos DBで作るAPIサーバ

Posted at

はじめに

AzureのサーバーレスAzure FunctionsとNoSQLストレージCosmos DBでAPIサーバを構築しました。

作ったもの → https://github.com/momotaro98/yarana-api

本記事ではその実装内容を記載します。

対象者

パブリッククラウドとしてAzureを採用している(しようとしている)人
APIサーバをサーバレスで構築しようとしている人

実装

APIを作っていきます。

Azure Functionsの実装では今回はC#のcsxスクリプトを採用しています。

本記事ではAzure Portal上でのAzure Functions構築方法は記載しません。以前に書いたAzure FunctionsでTable StorageをCRUDするまで手順という記事に記述しているので是非見てみてください。

本記事で扱うデータモデル&エンドポイント

以下のデータモデルを本記事では例として扱います。

koto.json
{
    "id":"mhlv43098tbsu7bgsc4voku398qx1410",
    "userId":"Ufea0979d178607eb0483c4f9cmhd3d25",
    "title":"筋トレ"
}

ID、ユーザーID、タイトル名の3項目あります。

また、以下の2つのエンドポイントを作ります。

POST
/koto
GET
/kotos?userId=string

データを登録する POST /koto

上記のデータモデルのJSONがPOSTでやってきた場合の処理です。Cosmos DBへデータを格納します。

Azure Functions上のソースコード

run.csx
#r "Newtonsoft.Json"

using System.Net;
using Microsoft.Azure.WebJobs.Host;
using Newtonsoft.Json.Linq;

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, IAsyncCollector<dynamic> kotoDocument, TraceWriter log)
{
    dynamic data = await req.Content.ReadAsAsync<object>();
    dynamic koto = JObject.Parse(data.ToString());
    kotoDocument.AddAsync(koto);
    return req.CreateResponse(HttpStatusCode.OK);
}
function.json
{
    "bindings": [
      {
        "authLevel": "function",
        "name": "req",
        "type": "httpTrigger",
        "direction": "in",
        "methods": [
          "post"
        ],
        "route": "koto"
      },
      {
        "name": "$return",
        "type": "http",
        "direction": "out"
      },
      {
        "type": "documentDB",
        "name": "kotoDocument",
        "databaseName": "Yarana",
        "collectionName": "Kotos",
        "createIfNotExists": false,
        "connection": "yarana_DOCUMENTDB",
        "direction": "out"
      }
    ],
    "disabled": false
  }

コードはたった4行で済みます。
Cosmos DBのコレクションと紐付いたIAsyncCollector<dynamic>型を利用することでPOSTでやって来るJSONをそのまま.AddAsyncメソッドでCosmos DBへ格納することができます。

Cosmos DBに登録されたデータを確認

スクリーンショット_2018-04-01_16_52_35.png

ドキュメントが追加されています。

データを取得する GET /kotos?userId=string

上記で作成したモデルを含むデータを取得します。ここではユーザーIDをURLのuserIdクエリで指定し、そのユーザーIDを持つドキュメントを返す機能を実装します。

Azure Functions上のソースコード

run.csx
#r "Newtonsoft.Json"

using System.Net;
using Newtonsoft.Json;
using System.Net.Http.Headers;

public static HttpResponseMessage Run(HttpRequestMessage req, IEnumerable<dynamic> documents, TraceWriter log)
{
    log.Info("C# HTTP trigger function processed a request.");

    // Parse url query parameter
    // ex. https://yarana-api.azurewebsites.net/api/kotos?userId=d59964bb713fd6f4f5ef6a7c7e029388
    string userId = req.GetQueryNameValuePairs()
        .FirstOrDefault(q => string.Compare(q.Key, "userId", true) == 0)
        .Value;
    if (userId == null)
        return req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a userId on the query string or in the request body");
    log.Info($"userId: {userId.ToString()}");

    // Query from documents in CosmosDB
    IEnumerable<dynamic> docs = documents.Where(doc => doc.userId == userId);

    // Create JSON to return
    string responseJSON = JsonConvert.SerializeObject(docs);
    log.Info($"JSON to response: {responseJSON}");

    // Create response
    var response = req.CreateResponse(HttpStatusCode.OK);
    response.Content = new StringContent(responseJSON);
    response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
    response.Content.Headers.ContentType.CharSet = "utf-8";
    return response;
}
function.json
{
  "bindings": [
    {
      "authLevel": "anonymous",
      "name": "req",
      "type": "httpTrigger",
      "direction": "in",
      "methods": [
        "get"
      ],
      "route": "kotos"
    },
    {
      "name": "$return",
      "type": "http",
      "direction": "out"
    },
    {
      "type": "documentDB",
      "name": "documents",
      "databaseName": "Yarana",
      "collectionName": "Kotos",
      "connection": "yarana_DOCUMENTDB",
      "direction": "in"
    }
  ],
  "disabled": false
}

Cosmos DBからデータ取得する場合はコレクションと紐付いたIEnumerable<dynamic>型を利用することでdocuments.Where(doc => doc.userId == userId)のようにC#の文法でクエリできます。

GETで得られるJSONを確認

上記FunctionのURLへGETすると以下のようにデータが取得されます。

result.json
[
    {
        "id":"mhlv43098tbsu7bgsc4voku398qx1410",
        "userId":"Ufea0979d178607eb0483c4f9cmhd3d25",
        "title":"筋トレ","_rid":"a4EkALKyCQAUAAAAAAAAAA==",
        "_self":"dbs/a4EkAA==/colls/a4EkALKyCQA=/docs/a4EkALKyCQAUAAAAAAAAAA==/",
        "_etag":"\"00006b00-0000-0000-0000-5aad29c30000\"",
        "_attachments":"attachments/",
        "_ts":1521297859
    },
    {
        "id":"l0wklkhyozgzg5m3095lmk6b8mf3tua6",
        "userId":"Ufea0979d178607eb0483c4f9cmhd3d25",
        "title":"英語","_rid":"a4EkALKyCQAVAAAAAAAAAA==",
        "_self":"dbs/a4EkAA==/colls/a4EkALKyCQA=/docs/a4EkALKyCQAVAAAAAAAAAA==/",
        "_etag":"\"00006c00-0000-0000-0000-5aad33a50000\"",
        "_attachments":"attachments/",
        "_ts":1521300389
    }
]

_self, _etag, _attachments, _tsは、Cosmos DBが管理するメタデータです。上記のコードではこれらが含まれてしまいます。
これらを含めないようにするには、返したい要素のみを持つエンティティ(クラス)を定義し、そのエンティティのJSONを返すような実装にする必要があります。

【おまけ】 Swaggerが使えるAzure FunctionsのAPI Definition

Azure FunctionsではAPI Definitionという機能が提供されており、Swagger規格でAPIドキュメントを管理できます。

以下スクショ

スクリーンショット 2018-04-01 16.07.29.png

Swagger UIが表示されます。

おわりに

サーバーレス技術はシンプルな機能を持つAPIを構築する場合、開発&運用のコストを抑える意味で力を発揮します。
また、NoSQLストレージを利用すればJSONをそのまま入れ込み、取得できるコードを書ける環境が整っているので実装が楽ちんです。

参考

19
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
18