Справочник
GraphQL API
Същото публикувано съдържание като в REST API, през един адрес и със схема, изградена от вашите колекции. Връзките се зареждат в същата заявка.
Адрес#
POST/graphql
Изпълнява заявка с JSON тяло { query, variables, operationName, extensions }.
GET/graphql
Същото, но полетата са параметри в адреса (variables и extensions са кодирани като JSON). Удобно зад CDN.
Адресът е без /v1: https://api.console.citronos.com/graphql. Приема същите ключове като REST (ck_pub_ за публикуваното съдържание, ck_prev_ за черновите) в Authorization: Bearer … или x-api-key и дели с тях ограничението на заявките за всеки ключ.
GraphQL е модул, който се включва за работното пространство, и е част от плановете от Professional нагоре. Докато е изключен, /graphql отговаря с 404 на ключовете на това работно пространство.
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);Вашата схема#
Всяко работно пространство има собствена схема, която се изгражда наново при промяна на колекция. Интроспекцията работи с всеки валиден ключ, така че GraphiQL, инструментите на Apollo или graphql-codegen могат да я прочетат. Имената идват от slug на колекциите:
| Slug на колекцията | Тип | Поле за списък | Поле за един запис |
|---|---|---|---|
products | Product | products(…): ProductList! | product(id, slug, …): Product |
other-products | OtherProduct | otherProducts | otherProduct |
gallery | Gallery | galleryList | gallery |
site-settings (единична) | SiteSetting | няма | siteSetting(…): SiteSetting |
- Всеки тип запис има
id,slug,publishedAt,updatedAtиsortOrder, а след тях по едно поле за всяко поле на колекцията. - Полето за списък връща
{ data: [Entry!]!, meta: PageMeta! }, катоmetaсъдържаtotal,page,pageSizeиpageCount. - Полетата за изображения и медия са обекти
Asset(url,width,height,alt,variants,focal), връзките са типът на свързания запис, а форматираният текст еStringс аргументformat: HTML | JSON(JSON идва като низ). - Полето за един запис връща
null, когато записът не съществува, вместо грешка. - Живите (външни) колекции не са в схемата. Няма мутации и абонаменти.
Филтри, сортиране и страници#
Полетата за списък приемат filter, sort, page, pageSize (най-много 100), locale и status. Филтрите следват REST: StringFilter и FloatFilter имат eq ne lt lte gt gte in notIn (низовете добавят contains), BooleanFilter има eq ne, а полетата със списъци приемат ListFilter { contains }. Най-много 10 условия. sort е едно { 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
}
}Езици#
Подайте locale на поле от първо ниво и всяка преведена стойност под него, включително във връзките, идва на този език, а при липса на езика по подразбиране. Без locale получавате езика по подразбиране. С псевдоними една заявка може да поиска два езика.
Чернови#
status: DRAFT чете работните копия. Изисква ключ ck_prev_; с ключ ck_pub_ заявката завършва с грешка FORBIDDEN. Свързаните записи винаги са в публикуваната си версия.
# 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#
Изпратете extensions.persistedQuery.sha256Hash (SHA-256 на текста на заявката в шестнадесетичен вид) вместо самата заявка. Ако API познава хеша, изпълнява запазената заявка; ако не, отговаря с PERSISTED_QUERY_NOT_FOUND и статус 200, а вие изпращате заявката веднъж заедно с хеша. Запазените заявки се пазят 24 часа за всяко работно пространство. Хеш, който не отговаря на заявката, връща 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);Кеширане#
Успешните отговори към ключове ck_pub_ се кешират от API за 60 секунди и идват с Cache-Control: public, max-age=60, stale-while-revalidate=300, ETag (така If-None-Match дава 304) и Vary: Authorization, x-api-key, Accept. Отговорите към ключове за преглед са no-store, а отговорите с грешки никога не се пазят в кеша на API.
Ограничения за дълбочина и сложност#
Заявката може да е дълбока най-много 6 нива и да струва най-много 1000. Поле-обект струва 1, поле-списък 1 плюс 10 пъти избраното в него, а скаларните полета и интроспекцията са безплатни. Над някое от ограниченията заявката се отхвърля, преди да се изпълни, с QUERY_TOO_DEEP или 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 }
}
]
}Грешки#
extensions.code | Кога |
|---|---|
BAD_USER_INPUT | Непознато поле или оператор във филтъра, твърде много филтри, непознат език, поле за един запис без id или slug. |
FORBIDDEN | status: DRAFT с ключ ck_pub_. |
QUERY_TOO_DEEP, QUERY_TOO_COMPLEX | Над ограниченията по-горе. |
PERSISTED_QUERY_NOT_FOUND | Непознат хеш: изпратете заявката заедно с него. |
PERSISTED_QUERY_HASH_MISMATCH | Хешът не е SHA-256 на заявката (HTTP 400). |
Грешките на ниво HTTP (лош ключ, изключен модул, ограничение на заявките) са като при REST; вижте грешки.
Справочникът описва само публичното API и следва договора му за съвместимост: публичните отговори само получават нови полета. Описанието на тези адреси във формат OpenAPI е на /developers/openapi.json.