Referință
GraphQL API
Același conținut publicat ca în REST API, printr-un singur punct de acces, cu o schemă construită din colecțiile dumneavoastră. Relațiile se rezolvă în aceeași cerere.
Punct de acces#
POST/graphql
Rulează o interogare cu un corp JSON { query, variables, operationName, extensions }.
GET/graphql
La fel, dar cu câmpurile trimise ca parametri de interogare (variables și extensions codificate ca JSON). Util în spatele unui CDN.
Calea nu conține /v1: este https://api.console.citronos.com/graphql. Acceptă aceleași chei ca REST (ck_pub_ pentru conținutul publicat, ck_prev_ pentru ciorne) în Authorization: Bearer … sau x-api-key și împarte cu acestea limita de cereri per cheie.
GraphQL este un modul pe care îl activați pentru spațiul de lucru, inclus începând cu planul Professional. Cât timp este dezactivat, /graphql răspunde cu 404 pentru cheile acelui spațiu de lucru.
curl https://api.console.citronos.com/graphql \
-H "Authorization: Bearer $CITRON_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query":"query ($n: Int) { products(pageSize: $n, locale: \"en\") { data { slug name } meta { total } } }","variables":{"n":10}}'type ProductsQuery = {
products: { data: { slug: string | null; name: string | null }[]; meta: { total: number } };
};
const data = await citron.graphql<ProductsQuery>(
`query Products($n: Int) {
products(pageSize: $n, locale: "en", filter: { category: { eq: "station" } }) {
data { slug name }
meta { total }
}
}`,
{ n: 10 },
);
console.log(data.products.meta.total);Schema dumneavoastră#
Fiecare spațiu de lucru are propria schemă, reconstruită la fiecare modificare a unei colecții. Introspecția funcționează cu orice cheie validă, așa că GraphiQL, instrumentele Apollo sau graphql-codegen o pot citi. Numele provin din slug-urile colecțiilor:
| Slug-ul colecției | Tip | Câmp de listă | Câmp individual |
|---|---|---|---|
products | Product | products(…): ProductList! | product(id, slug, …): Product |
other-products | OtherProduct | otherProducts | otherProduct |
gallery | Gallery | galleryList | gallery |
site-settings (instanță unică) | SiteSetting | niciunul | siteSetting(…): SiteSetting |
- Fiecare tip de înregistrare are
id,slug,publishedAt,updatedAtșisortOrder, apoi câte un câmp pentru fiecare câmp al colecției. - Un câmp de listă returnează
{ data: [Entry!]!, meta: PageMeta! }, cutotal,page,pageSizeșipageCountînmeta. - Câmpurile de imagine și media sunt obiecte
Asset(url,width,height,alt,variants,focal), relațiile au tipul înregistrării asociate, iar textul formatat este unStringcu argumentulformat: HTML | JSON(JSON-ul vine ca șir de caractere). - Un câmp individual returnează
nullcând înregistrarea nu există, în loc de o eroare. - Colecțiile live (externe) nu apar în schemă. Nu există mutații sau subscripții.
Filtre, sortare și paginare#
Câmpurile de listă acceptă filter, sort, page, pageSize (cel mult 100), locale și status. Filtrele urmează modelul REST: StringFilter și FloatFilter au eq ne lt lte gt gte in notIn (șirurile adaugă contains), BooleanFilter are eq ne, iar câmpurile de tip listă acceptă ListFilter { contains }. Cel mult 10 condiții. sort este un singur { field, direction }.
query Stations($n: Int, $locale: String) {
products(
filter: { category: { eq: "station" }, capacity: { gte: 5 } }
sort: { field: publishedAt, direction: DESC }
page: 1
pageSize: $n
locale: $locale
) {
data {
id
slug
code
name
body(format: HTML)
cover { url width height alt focal { x y } }
specRows { label value }
related { slug name }
}
meta { total page pageSize pageCount }
}
}query Product($slug: String!) {
product(slug: $slug, locale: "bg") {
slug
name
body(format: JSON)
datasheet { url mimeType }
}
siteSetting(locale: "bg") {
siteName
phone
}
}Limbi#
Transmiteți locale pe un câmp rădăcină și fiecare valoare localizată de sub acesta, inclusiv din relații, vine în limba respectivă, cu limba implicită ca rezervă. Fără locale primiți limba implicită. Cu ajutorul alias-urilor, o singură cerere poate solicita două limbi.
Ciorne#
status: DRAFT citește copiile de lucru. Necesită o cheie ck_prev_; cu o cheie ck_pub_ interogarea eșuează cu eroarea FORBIDDEN. Înregistrările asociate se rezolvă întotdeauna la versiunea lor publicată.
# Needs a ck_prev_ key; with a ck_pub_ key this is a FORBIDDEN error
query Drafts {
products(status: DRAFT, sort: { field: updatedAt, direction: DESC }) {
data { slug updatedAt name }
}
}Persisted queries automate#
Trimiteți extensions.persistedQuery.sha256Hash (SHA-256 hexazecimal al textului interogării) în locul interogării. Dacă API-ul cunoaște hash-ul, rulează interogarea stocată; dacă nu, răspunde cu PERSISTED_QUERY_NOT_FOUND și statusul 200, iar dumneavoastră trimiteți interogarea o singură dată, împreună cu hash-ul. Interogările stocate sunt păstrate 24 de ore pentru fiecare spațiu de lucru. Un hash care nu corespunde interogării primește 400 PERSISTED_QUERY_HASH_MISMATCH.
# 1. Ask by hash only (a GET, so a CDN can cache it)
curl -G https://api.console.citronos.com/graphql \
-H "Authorization: Bearer $CITRON_API_KEY" \
--data-urlencode 'variables={"n":10}' \
--data-urlencode 'extensions={"persistedQuery":{"version":1,"sha256Hash":"9d26ad0be5e0f576dba38d7c8579002c6c89e13f114bd8565dd622081ce7a346"}}'
# → {"errors":[{"message":"PersistedQueryNotFound","extensions":{"code":"PERSISTED_QUERY_NOT_FOUND"}}]}
# 2. Not known yet: send the query once with its hash; it is kept for 24 hours
curl https://api.console.citronos.com/graphql \
-H "Authorization: Bearer $CITRON_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query":"query ($n: Int) { products(pageSize: $n) { data { slug } meta { total } } }","variables":{"n":10},"extensions":{"persistedQuery":{"version":1,"sha256Hash":"9d26ad0be5e0f576dba38d7c8579002c6c89e13f114bd8565dd622081ce7a346"}}}'// persisted: true does both steps for you and uses GET by default
const data = await citron.graphql<{ products: { data: { slug: string | null }[]; meta: { total: number } } }>(
'query ($n: Int) { products(pageSize: $n) { data { slug } meta { total } } }',
{ n: 10 },
{ persisted: true },
);
console.log(data.products.meta.total);Cache#
Răspunsurile reușite pentru cheile ck_pub_ sunt păstrate în cache de API timp de 60 de secunde și includ Cache-Control: public, max-age=60, stale-while-revalidate=300, un ETag (astfel, If-None-Match produce un 304) și Vary: Authorization, x-api-key, Accept. Răspunsurile pentru cheile de previzualizare sunt no-store, iar răspunsurile cu erori nu sunt niciodată păstrate în cache-ul API-ului.
Limite de adâncime și complexitate#
O interogare poate avea cel mult 6 niveluri de adâncime și un cost de cel mult 1.000. Un câmp de tip obiect costă 1, un câmp de tip listă costă 1 plus de 10 ori ceea ce selectează, iar câmpurile scalare și introspecția sunt gratuite. Dacă depășește oricare dintre limite, interogarea este respinsă înainte de execuție, cu QUERY_TOO_DEEP sau QUERY_TOO_COMPLEX.
# Cost: every list multiplies what is inside it by 10
# products 1 + data (1 + 10 × related (1 + 10 × related (1 + 10 × related (1)))) = 1112
query TooWide {
products {
data { related { related { related { slug } } } }
}
}{
"errors": [
{
"message": "Query complexity 1112 exceeds the limit of 1000",
"extensions": { "code": "QUERY_TOO_COMPLEX", "complexity": 1112, "limit": 1000 }
}
]
}Erori#
extensions.code | Când |
|---|---|
BAD_USER_INPUT | Un câmp sau un operator de filtrare necunoscut, prea multe filtre, o limbă necunoscută, un câmp individual fără id sau slug. |
FORBIDDEN | status: DRAFT cu o cheie ck_pub_. |
QUERY_TOO_DEEP, QUERY_TOO_COMPLEX | Depășirea limitelor de mai sus. |
PERSISTED_QUERY_NOT_FOUND | Hash necunoscut: trimiteți interogarea împreună cu el. |
PERSISTED_QUERY_HASH_MISMATCH | Hash-ul nu este SHA-256 al interogării (HTTP 400). |
Erorile de transport (o cheie greșită, modulul dezactivat, limita de cereri) sunt erori HTTP, ca în REST; consultați erorile.
Această referință acoperă doar API-ul public și respectă contractul de compatibilitate al API-ului: răspunsurile publice pot doar să primească câmpuri noi. Descrierea OpenAPI a acestor rute se află la /developers/openapi.json.