Gli endpoint GraphQL in Data API Builder (DAB) consentono di eseguire query e modificare i dati con precisione.
Ogni query dichiara esattamente quali campi sono necessari e supporta gli argomenti per filtrare, ordinare e la paginazione dei risultati.
Per impostazione predefinita, DAB ospita l'endpoint GraphQL in:
https://{base_url}/graphql
Le entità esposte tramite la configurazione vengono incluse automaticamente nello schema GraphQL.
Ad esempio, se si dispone delle entità books e authors, entrambe vengono visualizzate come campi radice nello schema.
Annotazioni
Per esplorare lo schema e il completamento automatico dei campi, usare qualsiasi client o IDE GraphQL moderno (ad esempio Apollo, Insonnia o l'estensione graphQL Visual Studio Code).
Parole chiave supportate in Generatore API dati
| Concetto |
GraphQL |
Scopo |
| Proiezione |
Elementi |
Scegliere i campi da restituire |
| Filtraggio |
filtro |
Limitare le righe per condizione |
| Ordinamento |
orderBy |
Definire l'ordinamento |
| Dimensioni della pagina |
primo |
Limitare gli elementi per pagina |
| Continuazione |
dopo |
Continua dall'ultima pagina |
Struttura di base
Ogni query GraphQL inizia con un campo radice che rappresenta un'entità.
Tutte le richieste GraphQL usano POST per l'endpoint /graphql con un corpo JSON contenente la query.
{
books {
items {
id
title
year
pages
}
}
}
La risposta è un oggetto JSON con la stessa forma del set di selezione.
I dettagli sull'impaginazione e sugli errori vengono visualizzati solo quando sono applicabili.
Annotazioni
Per impostazione predefinita, DAB restituisce fino a 100 elementi per ogni query, a meno che non sia configurato diversamente (runtime.pagination.default-page-size).
POST https://localhost:5001/graphql
Content-Type: application/json
{
"query": "{ books { items { id title year pages } } }"
}
Successo:
{
"data": {
"books": {
"items": [
{ "id": 1, "title": "Dune", "year": 1965, "pages": 412 },
{ "id": 2, "title": "Foundation", "year": 1951, "pages": 255 }
]
}
}
}
Paginazione riuscita:
{
"data": {
"books": {
"items": [
{ "id": 1, "title": "Dune", "year": 1965, "pages": 412 },
{ "id": 2, "title": "Foundation", "year": 1951, "pages": 255 }
],
"hasNextPage": true,
"endCursor": "eyJpZCI6Mn0="
}
}
}
Errore:
{
"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 } } }"}'
Le classi di modello seguenti deserializzano le risposte 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; }
}
Chiamare l'API e deserializzare la risposta:
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 ?? [];
}
Le seguenti classi di dati modellano le risposte 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
Chiamare l'API e analizzare la risposta:
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]
La funzione seguente chiama l'API GraphQL:
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 ?? [];
}
Sintassi di esempio:
const books = await getBooks("https://localhost:5001");
console.log(`Fetched ${books.length} books from the API.`);
Tipi di query
Ogni entità supporta due query radice standard:
| Quesito |
Descrizione |
entity_by_pk |
Restituisce un record in base alla chiave primaria |
entities |
Restituisce un elenco di record che corrispondono ai filtri |
Esempio che restituisce un record:
{
book_by_pk(id: 1010) {
title
year
}
}
Esempio che restituisce un insieme di risultati:
{
books {
items {
id
title
}
}
}
Filtro dei risultati
Utilizzare l'argomento filter per limitare i record restituiti.
{
books(filter: { title: { contains: "Foundation" } }) {
items { id title }
}
}
Questa query restituisce tutti i libri il cui titolo contiene "Foundation".
I filtri possono combinare confronti con operatori logici:
{
authors(filter: {
or: [
{ first_name: { eq: "Isaac" } }
{ last_name: { eq: "Asimov" } }
]
}) {
items { first_name last_name }
}
}
Vedere il riferimento all'argomento di filtro per gli operatori supportati, ad esempio eq, neqlt, lte, e isNull.
Ordinamento dei risultati
L'argomento orderBy definisce la modalità di ordinamento dei record.
{
books(orderBy: { year: DESC, title: ASC }) {
items { id title year }
}
}
In questo modo vengono restituiti i libri ordinati in year ordine decrescente, quindi in base a title.
Per altre informazioni, vedere il riferimento all'argomento orderBy.
Limitazione dei risultati
L'argomento first limita il numero di record restituiti in una singola richiesta.
{
books(first: 5) {
items { id title }
}
}
Restituisce i primi cinque libri, ordinati per chiave primaria per impostazione predefinita.
È anche possibile usare first: -1 per richiedere le dimensioni massime della pagina configurate.
Altre informazioni sono disponibili nel primo riferimento all'argomento.
Risultati continui
Per ottenere la pagina successiva, utilizzare il parametro after con il cursore della query precedente.
{
books(first: 5, after: "eyJpZCI6NX0=") {
items { id title }
}
}
Il after token contrassegna dove è terminata la pagina precedente.
Per ulteriori informazioni, consultare il riferimento all'argomento successivo.
Selezione dei campi (proiezione)
In GraphQL scegliere esattamente i campi visualizzati nella risposta.
Non esiste alcun carattere jolly come SELECT *. Richiedere solo ciò di cui hai bisogno.
{
books {
items { id title price }
}
}
È anche possibile usare alias per rinominare i campi nella risposta:
{
books {
items {
bookTitle: title
cost: price
}
}
}
Per informazioni dettagliate, vedere le informazioni di riferimento sulla proiezione dei campi .
Modifica dei dati
Le mutazioni graphQL consentono di creare, aggiornare ed eliminare record a seconda delle autorizzazioni di entità.
| Mutazione |
Action |
createEntity |
Creare un nuovo elemento |
updateEntity |
Aggiornare un elemento esistente |
deleteEntity |
Rimuovere un elemento |
Annotazioni
Il suffisso _by_pk si applica solo alle query, ad esempio book_by_pk. I nomi delle mutazioni non includono questo suffisso: usare updateBook e deleteBook, non updateBook_by_pk o deleteBook_by_pk.
Importante
Le mutazioni graphQL richiedono un pool di connessioni di database attivo. Se la stringa di connessione imposta Pooling=False o MultipleActiveResultSets=False, le mutazioni hanno esito negativo con l'errore Implicit distributed transactions have not been enabled. Impostare Pooling=True e MultipleActiveResultSets=True (SQL Server) o l'equivalente nel tuo fornitore di database.
Suggerimento
Per le stored procedure esposte tramite GraphQL, DAB antepone execute al nome dell'entità. Ad esempio, un'entità stored procedure denominata GetBookById diventa executeGetBookById nello schema. Per altre informazioni, vedere procedure memorizzate.
Creare un nuovo record
Usare una create mutazione per aggiungere un nuovo elemento.
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);
}
Aggiorna un record esistente
Usare una update mutazione per modificare campi specifici in un elemento esistente.
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);
}
Eliminare un record
Usare una delete mutazione per rimuovere un elemento in base alla chiave primaria.
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);
}