Data API Builder (DAB) の GraphQL エンドポイントでは、データを精確にクエリおよび変更できます。
各クエリは、必要なフィールドを正確に宣言し、結果のフィルター処理、順序付け、ページングの引数をサポートします。
既定では、DAB は GraphQL エンドポイントを次の位置でホストします。
https://{base_url}/graphql
構成によって公開されるエンティティは、GraphQL スキーマに自動的に含まれます。
たとえば、 books エンティティと authors エンティティがある場合、どちらもスキーマのルート フィールドとして表示されます。
注
スキーマとオートコンプリート フィールドを調べるには、最新の GraphQL クライアントまたは IDE (Apollo、不眠症、Visual Studio Code GraphQL 拡張機能など) を使用します。
データ API ビルダーでサポートされているキーワード
| 概念 |
GraphQL |
Purpose |
| プロジェクション |
アイテム |
返すフィールドを選択する |
| フィルター |
フィルター |
条件で行を制限する |
| 並べ替え |
orderBy |
並べ替え順序を定義する |
| ページ サイズ |
まずは |
ページあたりのアイテム数を制限する |
| 継続 |
後 |
最後のページから続行する |
基本構造
すべての GraphQL クエリは、エンティティを表すルート フィールドで始まります。
すべての GraphQL リクエストでは、クエリを含む JSON 本文をPOSTとして/graphqlエンドポイントに送信します。
{
books {
items {
id
title
year
pages
}
}
}
応答は、選択セットと同じ図形を持つ JSON オブジェクトです。
改ページとエラーの詳細は、該当する場合にのみ表示されます。
注
既定では、特に構成されていない限り、DAB はクエリごとに最大 100 個の項目を返します (runtime.pagination.default-page-size)。
POST https://localhost:5001/graphql
Content-Type: application/json
{
"query": "{ books { items { id title year pages } } }"
}
成功:
{
"data": {
"books": {
"items": [
{ "id": 1, "title": "Dune", "year": 1965, "pages": 412 },
{ "id": 2, "title": "Foundation", "year": 1951, "pages": 255 }
]
}
}
}
ページネーションが成功しました:
{
"data": {
"books": {
"items": [
{ "id": 1, "title": "Dune", "year": 1965, "pages": 412 },
{ "id": 2, "title": "Foundation", "year": 1951, "pages": 255 }
],
"hasNextPage": true,
"endCursor": "eyJpZCI6Mn0="
}
}
}
エラー:
{
"errors": [
{
"message": "Could not find item with the given key.",
"locations": [{ "line": 1, "column": 3 }],
"path": ["book_by_pk"]
}
]
}
curl -X POST "https://localhost:5001/graphql" \
-H "Content-Type: application/json" \
-d '{"query": "{ books { items { id title year pages } } }"}'
次のモデル クラスは、DAB GraphQL 応答を逆シリアル化します。
using System.Text.Json.Serialization;
public class GraphQLRequest
{
[JsonPropertyName("query")]
public string Query { get; set; } = string.Empty;
[JsonPropertyName("variables")]
public object? Variables { get; set; }
}
public class GraphQLResponse<T>
{
[JsonPropertyName("data")]
public T? Data { get; set; }
[JsonPropertyName("errors")]
public List<GraphQLError>? Errors { get; set; }
[JsonIgnore]
public bool IsSuccess => Errors is null || Errors.Count == 0;
}
public class GraphQLError
{
[JsonPropertyName("message")]
public string Message { get; set; } = string.Empty;
[JsonPropertyName("path")]
public List<string>? Path { get; set; }
}
public class BooksResponse
{
[JsonPropertyName("books")]
public BooksResult? Books { get; set; }
}
public class BooksResult
{
[JsonPropertyName("items")]
public List<Book>? Items { get; set; }
[JsonPropertyName("hasNextPage")]
public bool HasNextPage { get; set; }
[JsonPropertyName("endCursor")]
public string? EndCursor { get; set; }
}
public class Book
{
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("title")]
public string Title { get; set; } = string.Empty;
[JsonPropertyName("year")]
public int? Year { get; set; }
[JsonPropertyName("pages")]
public int? Pages { get; set; }
}
API を呼び出し、応答を逆シリアル化します。
public async Task<List<Book>> GetBooksAsync()
{
var request = new GraphQLRequest
{
Query = "{ books { items { id title year pages } } }"
};
var response = await httpClient.PostAsJsonAsync("graphql", request);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<GraphQLResponse<BooksResponse>>();
if (result?.Errors?.Count > 0)
{
throw new Exception(result.Errors[0].Message);
}
return result?.Data?.Books?.Items ?? [];
}
次のデータ クラスは、DAB GraphQL 応答をモデル化します。
from dataclasses import dataclass, field
import requests
@dataclass
class Book:
id: int
title: str
year: int | None = None
pages: int | None = None
@dataclass
class GraphQLError:
message: str
path: list[str] | None = None
@dataclass
class BooksResult:
items: list[Book] = field(default_factory=list)
has_next_page: bool = False
end_cursor: str | None = None
@dataclass
class GraphQLResponse:
data: dict | None = None
errors: list[GraphQLError] | None = None
@property
def is_success(self) -> bool:
return self.errors is None or len(self.errors) == 0
API を呼び出して応答を解析します。
def get_books(base_url: str) -> list[Book]:
query = "{ books { items { id title year pages } } }"
response = requests.post(
f"{base_url}/graphql",
json={"query": query}
)
response.raise_for_status()
data = response.json()
if "errors" in data and data["errors"]:
raise Exception(data["errors"][0]["message"])
items = data.get("data", {}).get("books", {}).get("items", [])
return [Book(**item) for item in items]
次の関数は GraphQL API を呼び出します。
async function getBooks(baseUrl) {
const query = "{ books { items { id title year pages } } }";
const response = await fetch(`${baseUrl}/graphql`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query }),
});
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
const result = await response.json();
if (result.errors?.length > 0) {
throw new Error(result.errors[0].message);
}
return result.data?.books?.items ?? [];
}
使用例:
const books = await getBooks("https://localhost:5001");
console.log(`Fetched ${books.length} books from the API.`);
クエリの種類
各エンティティは、次の 2 つの標準ルート クエリをサポートしています。
| クエリ |
説明 |
entity_by_pk |
主キーで 1 つのレコードを返します |
entities |
フィルターに一致するレコードの一覧を返します。 |
1 つのレコードを返す例:
{
book_by_pk(id: 1010) {
title
year
}
}
多くの値を返す例
{
books {
items {
id
title
}
}
}
結果のフィルター処理
filter引数を使用して、返されるレコードを制限します。
{
books(filter: { title: { contains: "Foundation" } }) {
items { id title }
}
}
このクエリは、タイトルに "Foundation" が含まれるすべての書籍を返します。
フィルターは、比較と論理演算子を組み合わせることができます。
{
authors(filter: {
or: [
{ first_name: { eq: "Isaac" } }
{ last_name: { eq: "Asimov" } }
]
}) {
items { first_name last_name }
}
}
、eq、neq、lt、lteなどのサポートされている演算子については、isNullを参照してください。
結果を並べ替える
orderBy引数は、レコードの並べ替え方法を定義します。
{
books(orderBy: { year: DESC, title: ASC }) {
items { id title year }
}
}
これにより、 year 降順、 title順に並べ替えられた書籍が返されます。
詳細については、 orderBy 引数のリファレンスを参照してください。
結果を制限する
first引数は、1 つの要求で返されるレコードの数を制限します。
{
books(first: 5) {
items { id title }
}
}
これにより、既定で主キー順に並べ替えられた最初の 5 つの書籍が返されます。
first: -1を使用して、構成された最大ページ サイズを要求することもできます。
詳しくは、 最初の引数リファレンスをご覧ください。
継続的な結果
次のページをフェッチするには、前のクエリのカーソルと共に after 引数を使用します。
{
books(first: 5, after: "eyJpZCI6NX0=") {
items { id title }
}
}
after トークンは、前のページが終了した場所をマークします。
詳細については、 after 引数のリファレンスを参照してください。
フィールドの選択 (プロジェクション)
GraphQL では、応答に表示されるフィールドを正確に選択します。
SELECT *のようなワイルドカードはありません。 必要なものだけを要求します。
{
books {
items { id title price }
}
}
エイリアスを使用して、応答内のフィールドの名前を変更することもできます。
{
books {
items {
bookTitle: title
cost: price
}
}
}
詳細については 、フィールド プロジェクションリファレンス を参照してください。
データの変更
GraphQL の変更を使用すると、エンティティのアクセス許可に応じてレコードを作成、更新、削除できます。
| ミューテーション |
アクション |
createEntity |
新しいアイテムを作成する |
updateEntity |
既存のアイテムを更新する |
deleteEntity |
アイテムを削除する |
注
_by_pk サフィックスはクエリにのみ適用されます (たとえば、book_by_pk)。 変異名にはこのサフィックスは含まれません。updateBookやdeleteBookではなく、updateBook_by_pkとdeleteBook_by_pkを使用します。
Important
GraphQL の変更には、アクティブなデータベース接続プールが必要です。 接続文字列が Pooling=False または MultipleActiveResultSets=False設定されている場合、エラー Implicit distributed transactions have not been enabledで変更は失敗します。
Pooling=TrueとMultipleActiveResultSets=True (SQL Server) またはそれと同等のデータベース プロバイダーを設定します。
ヒント
GraphQL を介して公開されるストアド プロシージャの場合、DAB はエンティティ名の前に execute を付けます。 たとえば、 GetBookById という名前のストアド プロシージャ エンティティは、スキーマ内で executeGetBookById になります。 詳細については、 ストアド プロシージャを参照してください。
新しいレコードを作成します
新しい項目を追加するには、 create の変更を使用します。
POST https://localhost:5001/graphql
Content-Type: application/json
{
"query": "mutation { createBook(item: { id: 2000, title: \"Leviathan Wakes\", year: 2011, pages: 577 }) { id title year pages } }"
}
curl -X POST "https://localhost:5001/graphql" \
-H "Content-Type: application/json" \
-d '{"query": "mutation { createBook(item: { id: 2000, title: \"Leviathan Wakes\", year: 2011, pages: 577 }) { id title year pages } }"}'
var request = new GraphQLRequest
{
Query = @"
mutation CreateBook($item: CreateBookInput!) {
createBook(item: $item) { id title year pages }
}",
Variables = new
{
item = new { id = 2000, title = "Leviathan Wakes", year = 2011, pages = 577 }
}
};
var response = await httpClient.PostAsJsonAsync("graphql", request);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<GraphQLResponse<CreateBookResponse>>();
if (result?.Errors?.Count > 0)
{
throw new Exception(result.Errors[0].Message);
}
query = """
mutation CreateBook($item: CreateBookInput!) {
createBook(item: $item) { id title year pages }
}
"""
variables = {
"item": {"id": 2000, "title": "Leviathan Wakes", "year": 2011, "pages": 577}
}
response = requests.post(
f"{base_url}/graphql",
json={"query": query, "variables": variables}
)
response.raise_for_status()
data = response.json()
if "errors" in data and data["errors"]:
raise Exception(data["errors"][0]["message"])
const query = `
mutation CreateBook($item: CreateBookInput!) {
createBook(item: $item) { id title year pages }
}
`;
const variables = {
item: { id: 2000, title: "Leviathan Wakes", year: 2011, pages: 577 },
};
const response = await fetch(`${baseUrl}/graphql`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query, variables }),
});
const result = await response.json();
if (result.errors?.length > 0) {
throw new Error(result.errors[0].message);
}
既存のレコードを更新
既存の項目の特定のフィールドを変更するには、 update の変更を使用します。
POST https://localhost:5001/graphql
Content-Type: application/json
{
"query": "mutation { updateBook(id: 2000, item: { title: \"Leviathan Wakes\", year: 2011, pages: 577 }) { id title year pages } }"
}
curl -X POST "https://localhost:5001/graphql" \
-H "Content-Type: application/json" \
-d '{"query": "mutation { updateBook(id: 2000, item: { title: \"Leviathan Wakes\", year: 2011, pages: 577 }) { id title year pages } }"}'
var request = new GraphQLRequest
{
Query = @"
mutation UpdateBook($id: Int!, $item: UpdateBookInput!) {
updateBook(id: $id, item: $item) { id title year pages }
}",
Variables = new
{
id = 2000,
item = new { title = "Leviathan Wakes", year = 2011, pages = 577 }
}
};
var response = await httpClient.PostAsJsonAsync("graphql", request);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<GraphQLResponse<UpdateBookResponse>>();
if (result?.Errors?.Count > 0)
{
throw new Exception(result.Errors[0].Message);
}
query = """
mutation UpdateBook($id: Int!, $item: UpdateBookInput!) {
updateBook(id: $id, item: $item) { id title year pages }
}
"""
variables = {
"id": 2000,
"item": {"title": "Leviathan Wakes", "year": 2011, "pages": 577}
}
response = requests.post(
f"{base_url}/graphql",
json={"query": query, "variables": variables}
)
response.raise_for_status()
data = response.json()
if "errors" in data and data["errors"]:
raise Exception(data["errors"][0]["message"])
const query = `
mutation UpdateBook($id: Int!, $item: UpdateBookInput!) {
updateBook(id: $id, item: $item) { id title year pages }
}
`;
const variables = {
id: 2000,
item: { title: "Leviathan Wakes", year: 2011, pages: 577 },
};
const response = await fetch(`${baseUrl}/graphql`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query, variables }),
});
const result = await response.json();
if (result.errors?.length > 0) {
throw new Error(result.errors[0].message);
}
レコードを削除する
deleteの変更を使用して、主キーによって項目を削除します。
POST https://localhost:5001/graphql
Content-Type: application/json
{
"query": "mutation { deleteBook(id: 2000) { id title } }"
}
curl -X POST "https://localhost:5001/graphql" \
-H "Content-Type: application/json" \
-d '{"query": "mutation { deleteBook(id: 2000) { id title } }"}'
var request = new GraphQLRequest
{
Query = @"
mutation DeleteBook($id: Int!) {
deleteBook(id: $id) { id title }
}",
Variables = new { id = 2000 }
};
var response = await httpClient.PostAsJsonAsync("graphql", request);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<GraphQLResponse<DeleteBookResponse>>();
if (result?.Errors?.Count > 0)
{
throw new Exception(result.Errors[0].Message);
}
query = """
mutation DeleteBook($id: Int!) {
deleteBook(id: $id) { id title }
}
"""
variables = {"id": 2000}
response = requests.post(
f"{base_url}/graphql",
json={"query": query, "variables": variables}
)
response.raise_for_status()
data = response.json()
if "errors" in data and data["errors"]:
raise Exception(data["errors"][0]["message"])
const query = `
mutation DeleteBook($id: Int!) {
deleteBook(id: $id) { id title }
}
`;
const variables = { id: 2000 };
const response = await fetch(`${baseUrl}/graphql`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query, variables }),
});
const result = await response.json();
if (result.errors?.length > 0) {
throw new Error(result.errors[0].message);
}