3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Azure API Management の GraphQL API の リゾルバー で Cosmos DB データソースを使ってみる

Posted at

はじめに

API Managementは、API を簡単に管理できるサービスですが、REST API だけでなく GraphQL の API も作成できることができます。
API Management では、これまで既存の REST API をデータソースとして GraphQL の API を作成することができましたが、Cosmos DB と Azure SQL Database をデータソースとして使用できるようになりました。(※プレビュー段階)
これらのデータソースを使用することで、別途バックエンドを用意せずにデータベースを直接 API として公開することができます。
前回の記事では、Azure SQL Database のデータソースを利用しましたが、この記事では Cosmos DB のデータソースを使って、API Management で GraphQL の API を作成してみたいと思います。

Azure SQL Database 版

前提

  • API Management のリソースが作成済みであること。
    • この記事では API Management のリソース作成手順については説明しません。
    • 価格レベルは何でも良いと思いますが、執筆時は従量課金レベルで検証しています。
  • Cosmos DB のリソースが作成済みであること。
    • この記事では Cosmos DB のリソース作成手順については説明しません。
  • Azure CLI がインストール済みであること。
    • この記事では Azure CLI のインストール手順については説明しません。

事前準備

基本的にはドキュメントに従って進めていきます。

API Management のマネージド ID を有効にする

SQL Database 版と同様となります。

Cosmos DB のロールの割り当て

ターミナルで以下のコマンドを実行し、マネージド ID に Cosmos DB のロールを付与します。

bash
resourceGroupName="<リソースグループ名>"
cosmosName="<Cosmos DB のリソース名>"
apimName="<API Management のリソース名>"

# API Management のプリンシパルID を取得する
apimPrincipal=$(az apim show \
                    --resource-group $resourceGroupName \
                    --name $apimName \
                    --query "identity.principalId" \
                    --output tsv)

# Cosmos DB のリソースID を取得する
cosmosdbId=$(
    az cosmosdb show \
        --resource-group $resourceGroupName \
        --name $cosmosName \
        --query id \
        --output tsv
)

# API Management の マネージド ID に Cosmos DB のロールを付与する
# ここでは「Cosmos DB Built-in Data Contributor」を付与
az cosmosdb sql role assignment create \
    --resource-group $resourceGroupName \
    --account-name $cosmosName \
    --role-definition-name "Cosmos DB Built-in Data Contributor" \
    --principal-id $apimPrincipal \
    --scope $cosmosdbId

データベースとコンテナの作成

適当なデータベースとコンテナを作成し、適当なデータを投入します。

API の作成

Schema ファイルの作成

GraphQL API を作成する際に Schema ファイルを選択する必要があるため、事前に作成します。
今回は以下のように作成しました。

type Query {
  getUser(id: ID!): User!
  getUsers(): UserList!
}

type Mutation {
  createUser(input: CreateUserInput!): User!
  updateUser(input: UpdateUserInput!): User!
  deleteUser(id: ID!): Boolean!
}

type User {
  id: ID!
  name: String!
  age: Int!
}

type UserList {
  items: [User!]!
  hasNextPage: Boolean!
  endCursor: String
}

input UpdateUserInput {
  id: ID!
  name: String!
  age: Int!
}

input CreateUserInput {
  id: ID!
  name: String!
  age: Int!
}

GraphQL API の追加

SQL Database 版と同様となります。

Resolver の作成

作成した API が一覧に表示されるので、クリックすると先ほど選択した Schema が見られます。
更に行番号の左にあるをクリックすると、Resolver の登録画面に行くことができます。
image.png

Resolver の登録画面で Data source にAzure Cosmos DBを設定し、Resolver policy(後述)を入力して Create をクリック。
※Schema から ジャンプした場合は Name、Type、Field は自動的に入力されています
image.png

ポリシーの設定

接続設定(共通)

<cosmosdb-data-source>
    <!-- 接続設定 -->
	<connection-info>
		<connection-string use-managed-identity="true">
            AccountEndpoint=https://<Cosmos DB リソース名>.documents.azure.com:443/;
        </connection-string>
		<database-name>demo-db</database-name>
		<container-name>users</container-name>
	</connection-info>
	<!-- 省略 -->
</cosmosdb-data-source>

マネージドIDで接続するため、connection-stringの属性にuse-managed-identity="true"を指定します。
database-nameに Cosmos DB のデータベース名を指定します。
container-nameに Cosmos DB のコンテナ名を指定します。

単一結果の取得

単一データを取得する場合は以下の様なポリシーとなります。
(getUser)

<!-- getUser -->
<cosmosdb-data-source>
	<!-- 接続設定は省略 -->
	<read-request>
		<id>
            @(context.GraphQL.Arguments["id"].Value<string>())
        </id>
		<partition-key>
            @(context.GraphQL.Arguments["id"].Value<string>())
        </partition-key>
	</read-request>
</cosmosdb-data-source>

read-request要素を設定します。
SQL Database とは異なり、こちらはクエリを書かずにキー(idpartition-key)を指定するだけで OK です。
idpartition-keyではリクエストパラメータを参照することができます。
参照するには ポリシー式 を指定します。
context.GraphQL.Argumentsにリクエスト時に指定した引数が格納されています。

複数データの取得

複数行の結果を取得する場合は以下の様なポリシーとなります。
(getUsers)

<!-- getUsers -->
<cosmosdb-data-source>
	<!-- 接続設定は省略 -->
	<query-request>
		<sql-statement>
            SELECT * FROM c
        </sql-statement>
	</query-request>
</cosmosdb-data-source>

結果セットが複数行の場合は、SQL Database の場合と同様sql-statement要素にクエリを記述します。

また、こちらも SQL Database と同様parametersに指定したパラメータをsql-statement内で使用することができます。

<!-- getUsers -->
<cosmosdb-data-source>
	<!-- 接続設定は省略 -->
	<query-request>
		<sql-statement>
-           SELECT * FROM c
+           SELECT * FROM c WHERE CONTAINS(c.name, @name)
        </sql-statement>
+       <parameters>
+           <parameter name="@name">@(context.GraphQL.Arguments["name"])</parameter>
+       </parameters>
	</query-request>
</cosmosdb-data-source>

更新系

更新系の場合は以下の様なポリシーとなります。
(createUserの場合)

<!-- createUser -->
<cosmosdb-data-source>
	<!-- 接続設定は省略 -->
    <write-request type="upsert">
        <partition-key>
            @(context.GraphQL.Arguments["input"]["id"].Value<string>())
        </partition-key>
        <set-body template="liquid">
            {
                "id":"{{body.arguments.input.id}}",
                "name":"{{body.arguments.input.name}}",
                "age":{{body.arguments.input.age}}
            }
        </set-body>
    </write-request>
</cosmosdb-data-source>

write-request要素を設定します。
単一データの取得と同様、クエリを書かずにキー(partition-key)を指定します。
逆に言うと、クエリによる更新はそもそも Cosmos DB ではサポートされていないため、できません。
write-request要素のtype属性で更新の種類を指定します。
insertreplaceupsertが指定可能です。

2023/09/03 時点
試してみたところ、insertを指定してもupsertと同じ挙動だったり、ドキュメントにはreplaceを指定した場合はid要素が必須となっているけど、そもそもid要素が指定できなかったりとまだ怪しい感じでした

set-body要素で入力するデータ(JSON)を組み立てます。
SQL Database の方でも登場しましたが、Liquid というテンプレート構文で記述します。

削除

削除の場合は以下の様なポリシーとなります。
(deleteUser)

<!-- deleteUser -->
<cosmosdb-data-source>
	<!-- 接続設定は省略 -->
	<delete-request>
		<id>
            @(context.GraphQL.Arguments["id"].Value<string>())
        </id>
		<partition-key>
            @(context.GraphQL.Arguments["id"].Value<string>())
        </partition-key>
	</delete-request>
</cosmosdb-data-source>

delete-request要素を設定します。
read-requestと同様、キー(idpartition-key)を指定します。

API のテスト

テスト方法は SQL Database 版と同様です。

おまけ

ページング

Cosmos DB では、クエリによる複数データを取得する際、ページングを制御することができます。

Resolver ポリシー

<!-- getUsers -->
<cosmosdb-data-source>
	<!-- 接続設定は省略 -->
	<query-request>
		<sql-statement>
            SELECT * FROM c
        </sql-statement>
+		<paging>
+			<max-item-count>
+               @(context.GraphQL.Arguments["input"]["maxItemCount"]?.Value<string>())
+           </max-item-count>
+			<continuation-token>
+               @(context.GraphQL.Arguments["input"]["continuationToken"]?.Value<string>())
+			</continuation-token>
+		</paging>
    </query-request>
</cosmosdb-data-source>

paging要素を設定します。
max-item-count要素で1ページの取得件数を指定します。(デフォルトは 100)
continuation-token要素で次ページを特定するためのトークン(後述)を指定します。
max-item-countcontinuation-token共にポリシー式で記述することができます。

max-item-countは文字列として設定しないとエラーになりました

<max-item-count>
   <!-- ↓はエラー -->
   @(context.GraphQL.Arguments["input"]["maxItemCount"]?.Value<int>())
</max-item-count>

Schema

Schema は以下のように変更しています。

type Query {
- getUser(id: ID!): User!
+ getUsers(input: GetUserInput!): UserList!
  getUsers(): UserList!
}

type Mutation {
  createUser(input: CreateUserInput!): User!
  updateUser(input: UpdateUserInput!): User!
  deleteUser(id: ID!): Boolean!
}

type User {
  id: ID!
  name: String!
  age: Int!
}

type UserList {
  items: [User!]!
  hasNextPage: Boolean!
  endCursor: String
}

+input GetUserInput {
+  maxItemCount: Int
+  continuationToken: String
+}

"・・・省略・・・"

実行結果

最初はmax-item-countを指定せずに実行。
全部で 3 件取得できました。

image.png

レスポンスを見てみるとendCursorhasNextPageというプロパティがあります。

次にmax-item-countに 2 を指定してみます。

image.png

結果が 2 件のみとなりました。
また、endCursorhasNextPageの値が先ほどと変わりましたね。
全体の件数がmax-item-countを超えている場合はhasNextPagetrueとなり、endCursorに次ページを特定するためのトークンがセットされます。

では、continuation-tokenに上記のトークンを指定してみます。

image.png

先ほど取得されなかった残りの 1 件が取得できました。

その他

レスポンスのカスタマイズや、オプショナルなパラメータを指定する方法は SQL Database 版と同様、こちらでも使えます!

おわりに

API Management と データベースだけで GraphQL の API を作成することができました。
また、ここでは紹介できませんでしたが、データ更新の際にストアドプロシージャを呼び出してバリデーションするといったこともできるみたいです。
他にも紹介できていない機能もありますので、気になった方は遊んでみてください!

3
1
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
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?