GraphQL vs REST: Přehled

Před několika měsíci jsem napsal srovnání mezi RPC a REST pro časopis Smashing Magazine a nyní chci mluvit o rozdílech mezi REST a GraphQL: nové dítě v bloku.

GraphQL je některými nesprávně považován za „náhradu“ za REST. GraphQL je novější koncept, který Facebook veřejně vydal v roce 2015, zatímco REST byla dizertační práce publikovaná Royem Fieldingem v roce 2000, popularizovanou společnostmi jako Twitter (docela nepřesně) v roce 2006.

Cílem tohoto článku je pokrýt několik významných rozdílů a uvést následující body:

  1. REST a GraphQL jsou naprosto odlišné
  2. GraphQL není kouzelnou kulkou a není ani „lepší“
  3. Určitě můžete použít oba současně
  4. GraphQL je dope, pokud je použit pro správnou věc

REST je architektonický koncept pro síťový software, nemá žádnou oficiální sadu nástrojů, nemá žádné specifikace, nestará se, pokud používáte HTTP, AMQP atd., A je navržen tak, aby oddělil API od klienta. Důraz je kladen na to, aby API vydržely po celá desetiletí, namísto optimalizace výkonu.

GraphQL je dotazovací jazyk, specifikace a kolekce nástrojů, které jsou navrženy tak, aby fungovaly přes jediný koncový bod přes HTTP a optimalizovaly výkon a flexibilitu.

Jedním z hlavních nájemců REST je využít jednotné rozhraní protokolů, ve kterých existuje. Při použití HTTP může REST využívat typy obsahu HTTP, ukládání do mezipaměti, stavové kódy atd., Zatímco GraphQL využívá své vlastní konvence.

Dalším hlavním zaměřením RESTu jsou ovládací prvky hypermedie (a.k.a HATEOAS), které umožňují dobře navrženému klientovi běžet kolem API jako člověk běžící po Internetu; počínaje vyhledáváním „Jak dokončit daňové přiznání“, přečtením dokonale relevantního článku a po několika kliknutích končí článek BuzzFeed o Miley Cyrusové, jak hodí Liamovi Hemsworthovi oslavu narozenin „Weed-Themed“.

Pokud vaše rozhraní API nepoužívá ovládací prvky hypermedie, může být GraphQL relevantnějším přístupem, protože REST stejně nepoužíváte.

Tento článek se nebude pokoušet upozornit na vítěze, ale podíváme se na několik oblastí, kde se tyto dvě liší. Nenechte se naštvat, že mnoho sekcí říká „záleží“, protože vítěz v každé sekci opravdu záleží na tom, co vaše API dělá a jak. GraphQL vyjde silnější v některých oblastech, REST v jiných, a někdy jsou oba docela hrozné. Pojďme se pustit!

Jedním z nejběžnějších úkolů, které REST API poskytují, je CRUD přes JSON, ale může udělat mnohem více, než je nahrávání souborů.

Nahrávání obrázku do těla HTTP může vypadat trochu takto:

POST / avatary HTTP / 1.1
Hostitel: localhost: 3000
Typ obsahu: image / jpeg
Délka obsahu: 284
nezpracovaný obsah obrázku

Vývojáři API, kteří využívají skvělou část HTTP (a tedy REST), mohou podporovat žádosti aplikací / json ve stejném koncovém bodě, aby s nahráváním nakládali trochu jinak, a nabízejí také uploady založené na URL:

POST / avatary HTTP / 1.1
Hostitel: localhost: 3000
Content-Type: application / json
{
  "image_url": "https://example.org/pic.png"
}

Obecně API, na kterých jsem pracoval, si užily obojí, což je velmi užitečné, protože iOS často odesílá fotografie přímo z lokálních souborů a weboví klienti často odesílají URL na uživatelský obrázek na Facebooku uživatele.

Pokud bychom hovořili o nahrávání videí nebo jiných velkých souborů, přepnul bych (jak tento článek navrhuje) na jiný přístup a měl by vyhrazenou službu, která by se s nahráváním zabývala, přičemž hlavní API ponechává pouze metadata; název, popis, značky atd.

Toto je přístup, který jste nuceni přijmout s GraphQL, protože s GraphQL můžete mluvit pouze v oblastech:

POST / graphql HTTP / 1.1
Hostitel: localhost: 3000
Typ obsahu: application / graphql
mutation addAvatar {
  addAvatarFromUrl (image_url: "https://example.org/pic.png") {
    id,
    image_url
  }
}

Někteří budou argumentovat, že je to „čistější“, a je to velmi čisté, ale nucení vytvořit jinou službu je pro menší obrázky nadměrné, zvláště brzy. Dalším přístupem je nahrát přímo do Amazonu S3, nutit závislost na klientech a potenciálně nechat vaše tokeny uniknout, nebo… použít vícedílné nahrávání, což je super hackerský přístup, který závisí na tom, jestli to server a různí klienti mohou dokonce podporovat.

Toto je jedna z oblastí, kde REST drží silnou pozici. Někteří by řekli, že zacházení s CRUD a svévolným materiálem REST je matoucí, ale toto je hlavní nájemce toho, co dělá REST tak užitečným. REST API může dělat cokoli, nejen posílat pole dozadu a dopředu - i když se často používá REST.

Jednou z falešných inzerovaných výhod GraphQL, kterou jsem viděl navrhovanou (na několika místech), je to, že „nikdy nemusíte nic verzovat“.

Navrhovaný přístup spočívá v přidání nových polí a deprecate starých, což je koncept známý v REST jako evoluce.

Odmítnutí, komunikace s třetími stranami, sledování používání a odstraňování v přijatelném čase je přesně to, co mnoho REST API dělala navždy.

REST je o vývojové schopnosti, takže se nemusíte obviňovat ze špatných návyků nás RESTish people.

Ačkoli GraphQL a REST mohou (a měly by) verzi prostřednictvím evoluce stejně snadno, GraphQL opravdu pomáhá vývojářům API, pokud jde o deprecations.

Jednou z oblastí, kde GraphQL vyniká, je, že použití monitorovacího pole na technické úrovni je neuvěřitelně snadné. Klienti GraphQL jsou nuceni specifikovat pole, která chtějí v dotazu vrátit:

POST / graphql HTTP / 1.1
Hostitel: localhost: 3000
Typ obsahu: application / graphql
{
  želvy (id: "123") {
    délka,
    šířka,
    inteligence
  }
}

Sledování by bylo triviální, ale REST API se chová trochu jinak. Zatímco všechna rozhraní REST API zpřístupňují základní koncový bod prostřednictvím / želvy / 123, ne všechna API nabízejí řídké sady polí: / želvy / 123? Pole = délka, šířka, inteligence. Z těch, kteří to nabízejí, je to téměř vždy volitelné.

Pokud klient REST API volá / želvy / 123, mohl by v této odpovědi použít jakékoli pole. Představte si, že se API chystá zbavit inteligence (protože všechny želvy jsou géniové), jak vývojáři API vědí, kteří klienti toto pole používají?

Přístup RPC je vytvořit nový koncový bod / getTurtle2 (nebo / v2 / želvy / 123 v RPC API předstírající, že je REST API), a lidem říkat, aby tento nový použili.

Jedním z přístupů v REST API je zasílání upozornění e-mailem na jakoukoli e-mailovou adresu, která byla zadána, když se klient zaregistroval pro tokeny OAuth (jako dříve Facebook), možná nabízí příznak funkce, takže různí klienti mohou přepnout přepínač, když jsou připraveni.

Dalším přístupem by bylo vytvoření nové verze zdroje, což znamená, že místo volání GET pomocí Accept: application / vnd.turtlefans.com + v1 + json by měli začít volat s Accept: application / vnd.turtlefans.com + v2 + json .

Všechny výše uvedené přístupy trpí stejným problémem, a to, že celá verze může být nadbytečná pro jednoduchou změnu, a vy byste mohli nutit vývojáře, aby se podívali na upgrade verze, aniž byste museli.

Pokud například klient požaduje aplikaci / vnd.turtlefans.com + v1 + json a API odebírá inteligenci v v2, vývojáři API vědí, že neupustí v1, dokud nebudou upgradováni poslední klienti. Bohužel, pokud klienti volají v1, ale nepoužívají pole inteligence, vývojáři API nemají tušení! Vývojáři API pouze vědí, že klienti chtějí v1, ne to, co používají tento zdroj v1.

GraphQL usnadňuje sledování využití konkrétního pole pro klienta, což znamená, že vlastníci API mohou oslovit pouze ty klienty, kteří používají pole, která směřují ven, nebo pro interní projekty byste mohli mít chyby vyvolávané v vývojových / pracovních prostředích.

Měl jsem nejasný mozek prd o tom, jak udělat tuto poslední možnost pro non-GraphQL HTTP API, ale neměl jsem šanci to prozkoumat.

https://twitter.com/philsturgeon/status/631588563524694016

To by mohlo v některých situacích pomoci, ale určitě by to bylo zapečeno do věcí stejným způsobem jako v GraphQL.

GrafQL, který usnadnil depreciaci pole, byl bodem, na který mě upozornil Tom Clark, super inteligentní vedoucí oddělení Devops ve společnosti WeWork.

GraphQL je vždy nejmenší možný požadavek, zatímco REST je standardně nastaven na maximum. Běžnou praxí je nabízet možnosti jako? Field = foo, bar nebo partials. Google to doporučuje pro HTTP API, ať už je to cokoli stojí za to.

I když rozhraní REST API vrací ve výchozím nastavení pouze základní částečnou část, ve výchozím nastavení je stále více bitů přenášeno než pomocí přístupu GraphQL. Pokud klient potřebuje pole, vyžádá si jej a pokud API přidá nové pole, klienti jej nezískají, pokud nezjistí toto pole v blogu nebo cokoli a nepřidají jej do dotazu GraphQL.

Ukládání do mezipaměti pro HTTP, běžné použití v REST API a různé typy ukládání do mezipaměti jsou obrovská témata a něco, co jsem musel z tohoto článku rozdělit. Brzy zveřejním následné sledování, které bude zveřejněno v API, které jste nenáviděli.

V rozhraní API založeném na koncových bodech mohou klienti používat ukládání do mezipaměti HTTP, aby se snadno vyhnuli opětovnému načtení prostředků a pro identifikaci, kdy jsou dva zdroje stejné. URL v těchto API je globálně jedinečný identifikátor, který může klient využít k vytvoření mezipaměti.
V GraphQL však neexistuje primitiv podobný URL, který poskytuje tento globálně jedinečný identifikátor pro daný objekt. Je tedy nejlepší praxí API odhalit takový identifikátor, který mohou klienti použít.
- Zdroj: graphql.org

REST over HTTP používá celou hromadu konvencí HTTP, které činí stávající klienty HTTP, proxy proxy HTTP cache atd., To vše snadno pracuje ve prospěch klientů API i serverů API, ale s GraphQL… těžkou. Reorganizujte své datové úložiště, použijte spoustu Redis a doufejte, že klienti také ukládají do mezipaměti.

Velmi zásadním rozdílem je zde samozřejmě to, že pouze jedním z nich je dotazovací jazyk. REST API jsou často vytvářena zpočátku jednoduše, pak se postupně a stále více řeší funkce podobné jazyku dotazů.

Nejrozumnějším způsobem, jak poskytnout argumenty pro dotazy v RESTu, je strčit je do řetězce dotazu. Možná? Status = aktivní pro filtrování podle stavu, pak pravděpodobně sort = created, ale klient potřebuje směr řazení, takže je přidán sort-dir = desc.

Některá rozhraní API také používají argumenty v řetězci dotazu, které nesouvisejí s filtrováním, spíše jako možnosti. V příkladu GraphQL určují jednotku, ve které by chtěli vidět vrácenou výšku:

{
  člověk (id: "1000") {
    název
    výška (jednotka: FOOT)
  }
}

To je docela zatraceně užitečné a zastavuje to zmatení? Status = active being filter, ale? Unit = foot je možnost zobrazení. Viděl jsem? Filter: status = active,? Filter [status] = active, atd., Ale tohle je pořád trochu nepořádek.

V těchto scénářích GraphQL bije kalhoty z REST API, které se pokoušejí odevzdat svou vlastní funkci dotazovacího jazyka, ale navrhuji, aby ruční rozbalení vlastního dotazovacího jazyka bylo stejně špatný nápad. GraphQL vám dává syntaxi dotazovacího jazyka a sady SDK pro váš programovací jazyk k tomu, aby za tím účelem byly získány správné modely, ale dělejte věci jako OData, projekt se sloganem „Lepší cesta k obnovení“.

Toto je další příklad lidí, kteří říkají, že REST nemůže něco udělat, jen proto, že to mnoho REST API neudělá, nebo proto, že je mnoho implementovaných špatně. Řekněme, že čím více přizpůsobení koncový bod API přidává k požadavku, je méně požadavků na mezipaměť pravděpodobně na úrovni mezipaměti sítě, takže přístup REST / HTTP je méně obohacující a nutí vás cestou mezipaměti aplikace a reorganizace databáze, kterou GraphQL každopádně nutí.

Pokud je vaše API vysoce přizpůsobitelné, je to další zaškrtnuté políčko pro zvážení GraphQL.

Dalším aspektem přizpůsobení, který přichází hodně, je, kdy nabídnout zahrnuté vztahy a kdy použít jiný koncový bod. To může být obtížná volba designu, protože chcete, aby vaše API bylo flexibilní a výkonné, ale zahrnuje použitá minulá období, protože nejzávažnější použití může být opakem.

Začínáte s příliš zjednodušujícími příklady, jako jsou / users? Include = comments, posts, ale nakonec to /trips?include=driver,passengers,passengers.avatar,passengers.itineraries and horší.

Říkám tomu „Mega Include of DOOOOOOM“ a jedná se o svinské řešení opravdového koncernu, když se klienti snaží dosáhnout výkonu nad všemi ostatními. Obsahuje běžnou konvenci nalezenou v REST API, doporučenou ve specifikacích, jako je JSON API, ale ve skutečnosti pravidla REST trochu ohýbá.

REST by vyžadoval přístup HATEOAS, který by vám umožnil jedno volání do koncového bodu / výlety, poté stiskl "odkazy": {"driver": "https://example.com/drivers/123"} a znovu pro cestující a znovu pro údaje o dítěti každého z těchto cestujících. V tomto případě by to bylo horší než obávaný n + 1 a spíš n + (2+ (počet cestujících * 2))!

Zahrnuje začátek s nejlepšími úmysly, ale může se stát překážkou v REST API, s nepraktickými dotazy se děje proti datovému úložišti. GraphQL nejen odstraní tuto návrhovou otázku, ale nutí přístup zahrnout, a nutí vás, abyste zvážili efektivní způsoby načítání těchto dat.

Pro GraphQL je to velká výhra, protože prosazení přístupu zahrnutí a vynucení efektivního „Mega Includes of Doom“ bude GraphQL efektivní a konzistentní. Pokus o to, aby REST API bylo jen zahrnutí, by bylo bizarní a nepoužitelné, takže zde nebude nikdy fungovat konzistence.

GraphQL nepomůže s vašimi databázovými dotazy, takže je třeba inteligentně vyladit tyto indexy a fragmenty mezipaměti dat. Pokud toto přeskočíte, klienti vás překvapí. Při předchozí práci jsme měli špatně vyladěnou mega, včetně aplikace pro iOS, která na odpověď reagovala asi 20 s +, což bylo krvavé děsivé. Téměř jsme je nechali stočit na nejnovější.sqlite, aby se zpracovaly lokálně.

Při použití tohoto příkladu cesty může klient zahrnout i cestující, ale uvědomit si, že zahrnuje i historické cestující. Nechcete vidět lidi, kteří opustili bazén? Klient musí iterovat prostřednictvím cestujících lokálně a odstranit všechny modely, kde model.status! = "Active", což je plýtvání se zpracováním na straně klienta.

Nejlepší způsob, jak toho dosáhnout, by bylo použít / cestující? Výlet = 123 & status = aktivní, což je samozřejmě flexibilnější, ale klienti to přeskočí kvůli požadovaným zvláštním požadavkům.

Vzhledem k tomu, že filtrování na straně klienta je špatnou volbou a zvláštní požadavek není ideální, vývojáři rozhraní REST API jsou často nuceni přidat nový příkaz include: / Trip? Include = activePassengers. Je těžké je předběžně vyprázdnit, takže jsem doufal, že GraphQL pomůže klientům definovat jejich vlastní rozsahy. Filtrování těchto zahrnutí je samo o sobě vhodnými daty, což by pomohlo identifikovat rozsahové zahrnutí, které by API mělo přidat jako pohodlné metody.

Zdá se, že GraphQL v tomto případě vývojářům API nepomáhá, ale zdá se, že se mluví o přidání @filteru, aby se to v budoucnu stalo.

Další otázka, která se pro vývojáře REST API hodně objevuje, je:

Naše aplikace pro iOS, aplikace pro Android a webová aplikace se od sebe velmi liší. Jak bychom vrátili různá data pro každého klienta?

Všichni se začínáme snažit, aby naše REST API byla tak obecná, že je bude možné použít cokoli, ale jak je uvedeno v mega zahrnutém problému, klienti chtějí a potřebují hodně a vždy se snaží omezit hovory.

Některá základní řešení pro výstup různých dat na klienta v REST API jsou:

Vytvořte vlastní koncové body: / ​​iphone_snapshot. Udělali jsme to v minulé společnosti, kde byla mobilní data úplně jiná než cokoli jiného. Cítil se špinavý, byl to téměř určitě RPC, ale práce byla dokončena. Mít údajně obecné REST API vědět tolik o konkrétním klientovi trochu porazí účel, ale my jsme potřebovali získat data do iPhone a to se stalo.

Vytvářejte vlastní reprezentace: Pomocí Content-Type: application / vnd.turtlefans.com + v1 + iphone + json, který měl svůj vlastní sériový serializátor, by údajně bylo o něco více REST v tom, že je to jen další reprezentace, ale stejně zvláštní.

Vlastní API! Tento koncept byl / je používán Netflixem, pokud si dobře pamatuji, a myšlenkou je mít pro každého ze svých klientů jedno API. K dispozici je rozhraní Android REST API, rozhraní iOS REST API, Web REST API atd. Každé z nich je uzpůsobeno tak, aby dokonale odpovídalo potřebám jejich konkrétních týmů, a zadává požadavky zpět do centrálního generického rozhraní REST API. Vyžadovat více API, více vývojových týmů atd., Je jistě mimo dosah mnoha organizací, ale problém to pěkně vyřeší.

Nebo ... GraphQL! Místo vytváření vlastních koncových bodů, vlastních reprezentací nebo vlastních API klienti jednoduše píšou své vlastní dotazy. Tím se odpovědnost přesouvá z rukou vývojářů API a na klienty, kteří se pak píšou v jazyce, který si zvolili, namísto bugování týmu API, aby jej napsal v jiném jazyce.

Ať už potřebujete tento posun paradigmatu nebo ne, záleží zcela na tom, jak podobní jsou vaši klienti. Pokud jste soukromým / interním rozhraním API a všichni vaši klienti jsou prakticky totožní, pak je určitě nechcete, aby se s nimi všichni zabývali.

Pokud však máte mnoho různých klientů nebo jste veřejní (proto nemáte ponětí, jak bude API použito), GraphQL se rychle začne jevit přitažlivější.

Největší podivnost, kterou jsem si všiml v konverzaci „GraphQL vs REST“, je nepravda, kterou si musíte vybrat.

Ve světě SoA budete pravděpodobně mít více služeb, které vystavují více API. V článku RPC vs REST poukazuji na to, že některé služby mohou být REST a některé mohou být RPC, a můžete absolutně hodit nějakou GraphQL do svého REST.

Jedna kombinace REST a GraphQL by mohla být pouze přidání koncového bodu / graphql do api.whatever.com a mít ho jako koncový bod GraphQL na rozhraní REST API.

Další je ten, který byl v práci nejasně přehazován, což je myšlenka mít jedno GraphQL API, které bude bránou k našim dalším více REST API.

Jeden server GraphQL funguje jako druh datového proxy, což dává jeden vstupní bod pro smíšená data, jedno schéma ověřování navzdory tomu, že každé rozhraní REST API má svůj „jedinečný“ přístup k tokenům, jedno volání HTTP pro klienty - navzdory tomu, že zasáhlo více skutečných rozhraní REST API atd. by byla zatraceně mocná věc.

Položte si - přinejmenším - následující otázky:

  • Jak se liší vaši klienti od sebe?
  • Důvěřujete svým klientům, že zvládnou ukládání do mezipaměti?
  • Chcete „hloupé klienty“, kteří se chovají jako prolézací moduly - vědí jen velmi málo o API, nebo klienty, kteří vlastní hodně logiky a znají hodně o API a používají je jednoduše jako přenos dat?
  • Jste v pořádku pustili HTTP debugovací proxy, proxy cache, všechny znalosti, které váš tým má kolem HTTP atd.?
  • Děláte jen základní CRUD s jednoduchými dokumenty JSON, nebo bude vaše API také vyžadovat upload / download souboru?

Pokud vaše REST API dodržuje osvědčené postupy, jako je umožnění pečlivého vývoje namísto globálního verzování, serializace dat namísto návratu přímo z úložiště dat, implementace řídkých polí, které umožňují zúžení velikosti odpovědí, obsah GZiping, načrtnutí datových struktur pomocí schématu JSON, nabídka binárních alternativy k JSON, jako Protobuff nebo BSON atd., pak se zdá, že inzerované výhody GraphQL jsou trochu krátké.

Pokud potřebujete rozhraní API s vysokým dotazem, očekáváte řadu klientů, kteří potřebují malá a různá data, a dokážete vaše data restrukturalizovat tak, aby byly levné, aby vyhovovaly vašim požadavkům.

Kromě těchto různých výhod a nevýhod pro GraphQL uvedených výše, co mě opravdu baví, protože GraphQL je možnost, má při zvažování API novou alternativu k REST. Alternativa, která je dobře zdokumentována, s úplnou specifikací, s krásnou marketingovou stránkou, s oficiální implementací referencí v JavaScriptu a která se vyhýbá některým obtížným možnostem designu REST, které vás nutí udělat.

Líbí se mi, že mnoho lidí už nebude REST chovat jako lesklý jednorožec, bude se snažit správně REST implementovat, pak to nazvat REST, aby vypadali dobře a měli pro svůj marketing . Jen doufám, že příliš mnoho lidí nebude zacházet s GraphQL jako s lesklým jednorožcem.

Výměna jednoho falešného idolu za jiný neznamená, že svět API bude lepším místem.

Původně zveřejněno na philsturgeon.uk dne 24. ledna 2017.