GraphQL

dotazovací jazyk pro tvorbu API

GraphQL je specifikace dotazovacího jazyka pro tvorbu API. Existuje několik různých přístupů k tvorbě API, nejznámější konkurencí je však REST. Při porovnání právě s REST architekturou je jednou z velkých výhod to, že v GraphQL je potřeba specifikovat konkrétní data, která ze serverové části mají přijít na klientskou. Uživatel tak nedostává data, o která si explicitně neřekl, a tím se eliminuje přenos nepotřebných dat. GraphQL je silně typované. Pokud tedy chceme pracovat s nějakou entitou, musí být pro ni nadefinovaný typ. Pokud by byl zaslán požadavek na neznámý typ, volání skončí neúspěšně.

GraphQL
Logo
VývojářFacebook
První vydání2. července 2015
Operační systémmultiplatformní
Licence3-clause BSD License
Webgraphql.org
Některá data mohou pocházet z datové položky.

Vychází z matematické teorie grafů, kde se dle této teorie grafy skládají z vrcholů (nebo také uzlů) a hran. Pro GraphQL to pak prakticky znamená, že veškerá data musí být uspořádána do datových typů a každý tento typ v teorii grafů znázorňuje jeden vrchol. Hrany spojující tyto vrcholy pak vyjadřují vztahy mezi těmito datovými typy.

GraphQL bylo vytvořeno v roce 2012 společností Facebook za účelem zlepšení jejich mobilní aplikace. O tři roky později se objevuje jako open-source. V roce 2018 byla vydána poslední nejaktuálnější stabilní verze a na další se již pracuje (koncem roku 2020 byl vydán pre-release).

Rozdíl mezi GraphQL a REST

editovat

Typickou charakteristikou pro REST je, že má mnoho endpointů. I pro jednodušší operaci je tedy často zapotřebí poslat na server hned několik požadavků (requestů), zatímco GraphQL má endpoint pouze jeden (typicky označován jako /graphql). Současně se vždy musí přesně definovat, o která data se žádá. Eliminuje se tak nejenom množství příchozích dat, ale i množství požadavků, které je potřeba na server odeslat. V GraphQL tedy neexistuje způsob, jak se obecně doptat na všechno, jako je typické například u dotazovacího jazyka SQL (př.: SELECT * FROM table;). Pokud se chceme dotázat na vše, musíme explicitně vyjmenovat všechny atributy.

Mezi další významné rozdíly se řadí to, že GraphQL je nezávislé na protokolu. Oproti tomu REST je určen výhradně pro práci s daty pomocí metod odpovídajících HTTP protokolu. Z toho pak vyplývá i rozdílná práce s chybovými hláškami. Zatímco u REST architektury dostaneme v odpovědi příslušný HTTP status, na který lze následně reagovat, tak u GraphQL je přístup kvůli nezávislosti na protokolu odlišný. Nejpoužívanější přístup, který se objevuje i u většiny knihoven, obsahuje jen dva statusy. Těmi jsou status 400 pro chybný požadavek a status 200 pro vše ostatní. Pokud dojde k nějaké chybě, tak popis této chyby se následně objeví typicky v JSON odpovědi od serveru v části pojmenované „error“.

Schéma a typy

editovat

V GraphQL jsou 3 základní objektové typy – Query, Mutation a Subscription. Tyto základní typy jsou tzv. entrypointy do GraphQL. Typ Query je povinný a je v něm definován seznam všech dotazů (queries), které je možné skrze GraphQL volat. Pokud by typ Query nebyl vůbec definovaný nebo by neměl definovanou alespoň jednu položku, tak API nedokáže vrátit žádná data, a je tak prakticky téměř nepoužitelné. Jak tedy částečně vyplývá z předchozí věty, typy se skládají z jednotlivých položek (fields).

Dále se dají definovat takzvané objektové custom typy. Těmi lze nadefinovat návratovou hodnotu jednotlivých položek (queries, mutations, subscriptions nebo položek jiných custom typů). Tyto custom typy jsou samozřejmě znovu použitelné, takže jejich vhodné nadefinování dokáže velmi zpřehlednit a zkrátit výsledný kód.

Návratovou hodnotou položky může být buďto některý z definovaných custom typů nebo některý ze základních typů tzv. skalární typ. Defaultními skalárními typy jsou pro GraphQL Int, Float, String, Boolean a ID (unikátní identifikátor reprezentovaný jako String). Speciálním skalárním typem je typ enumeration (enum), neboli výčtový typ. Stejně jako je možné definovat objektové custom typy, tak je možné definovat i skalární custom typy. Často používaným skalárním custom typem je například typ „Date“ pro datum. Návratová hodnota může být definována i jako pole skalárních či objektových typů. Návratové hodnoty lze ještě omezit tak, aby nebylo povoleno vracet hodnotu null. To se značí přidáním vykřičníku za danou návratovou hodnotu či pole hodnot.

Základem je pak takzvané schéma, což je kolekce všech objektových typů a jejich definic. Schématem se v podstatě definuje, jaká data mohou být posílána, přijímána a jaké jsou mezi nimi vazby. Na základě tohoto schématu je posléze možné vygenerovat dokumentaci, takže se o její tvorbu již není potřeba starat. Schéma je zpravidla definováno na jednom místě (např. pro JavaScript v souboru index.js). Pro rozsáhlejší aplikace může však schéma narůstat do obrovských rozměrů a stane se tak velmi nepřehledné. Existují proto nástroje, za pomoci kterých je možné schéma rozložit do více souborů.

Pro lepší představu jsou níže uvedeny dva ukázkové provázané custom typy (Author a Book) a k nim nadefinované jednoduché dotazy.

Příklad

editovat

Typ Author

editovat
type Author {
  id: ID!
  name: String!
  birthYear: Int!
  birthplace: String!
  books: [Book!]!
}

Typ Book

editovat
type Book {
  id: ID!
  name: String!
  description: String!
  releaseYear: Int!
  isbn: String!
  authors: [Author!]!
  authorsIds: [ID!]!
}

Typ Query

editovat
type Query {
  books: [Book!]!
  book(id: ID!): Book!
  authors: [Author!]!
  author(id: ID!): Author!
}

Resolvery

editovat

Resolvery jsou funkce v daném programovacím jazyce umožňující propojit schéma s cílovými daty. Je podporováno plno programovacích jazyků, mezi které patří například JavaScript, Java, C#, Python, PHP a další. Resolvery se dají rozdělit na dva typy. Prvním typem jsou resolvery pro základní objektové typy – Query, Mutation a Subscription. Tento typ slouží k tomu, aby tyto resolvery reálně vytáhly data, o která je žádáno, z cílového zdroje (databáze, soubory, další servery apod.). Druhým typem jsou resolvery pro objektové custom typy. Ve chvíli, kdy se specifikuje vztah mezi dvěma typy, musí se specifikovat i příslušný resolver.

V podstatě každá položka ze schématu musí mít svůj resolver, ne všechny je však potřeba definovat explicitně. Jednoduché resolvery dokáže většina GraphQL serverů vytvořit sama bez ruční definice. Na to se lze však spolehnout jen za předpokladu, že se atribut v objektovém typu jmenuje stejně, jako atribut, který přichází v objektu ze zdroje (opět z databáze, souborů apod.). Pokud se však jedná o resolvery, které s daty nějak dále pracují (například filtrace), je potřeba je definovat ručně.

Zjednodušeně by se tedy dalo říct, že schéma popisuje, jaká data jsou dispozici, zatímco resolvery pak s danými daty na základě příchozích queries, mutations a subscriptions už přímo nějak pracují.

Queries

editovat

Queries neboli dotazy slouží pro získání dat. Z pohledu CRUD tedy zastupují písmeno R (read/retrieve) a z pohledu RESTu se dají přirovnat k metodě GET. Dotazy se skládají ze 4 částí. První částí je typ operace, což může být query, mutation nebo subscription. Druhou částí je pak název operace. Další dvě části už reprezentují dotaz na data, kde první z nich je název dotazu definovaného ve schématu a pak následuje výčet položek daného typu, na které se chceme dotázat.

query Authors {
  authors {
    name
    birthYear
    birthplace
  }
}

Mezi výhody GraphQL patří i to, že nám umožňuje dotazy cyklit. Můžeme si to představit na jednoduchém příkladu již dříve definovaných knih a autorů. Každý autor může napsat více knih a každá kniha může mít více autorů. Mohli bychom například chtít vytvořit dotaz na základní informace o autorech včetně toho, jaké knihy napsali. U každé knihy by nás pak zajímaly informace i o konkrétní knize. Jednou ze základních informací je ovšem i to, jaké má autory (může jich mít více). Hodilo by se nám tedy dále i u knihy zjistit, kdo všechno se na její tvorbě podílel. V GraphQL se na tuto informaci můžeme jednoduše dotázat jedním dotazem.

query AuthorsAndBooks {
  authors {
    name
    birthYear
    books {
      name
      description
      authors {
        name
      }
    }
  }
}

Mutations

editovat

Mutations neboli mutace zastupují zbylé operace z pohledu CRUD, tedy přidávání, aktualizování a mazání. Zatímco dotazy data pouze načítají, mutations s daty manipulují. Z pohledu RESTu tedy zastupují metody PUT, POST a DELETE.

Subscriptions

editovat

Třetím typem operace je takzvaný subscription, který podobně jako dotazy čte data. V porovnání s dotazy se však jedná o aktivní spojení se serverem a tím pádem nám poskytuje vždy aktuální data (tzv. real-time data). To je vhodné například pro zasílání notifikací nebo pro tvorbu chatovacích aplikací.

Externí odkazy

editovat