Skip to content
Language:hu Author:Laborci Gergely
web-api

GraphQL és RPC: két másik API-szemlélet

Célok

A lecke végére meg tudod mondani, miben tér el a GraphQL és az RPC a REST erőforrásmodelljétől, le tudsz olvasni egy egyszerű GraphQL-lekérdezést és egy JSON-RPC-hívást, és érted, hogy a három stílus közti választás a feladat függvénye.

Miért kell más szemlélet is?

Nem minden adatcsere azonosítható erőforrások köré szerveződik. Egy mobilkijelző a kurzus címét, oktatóját és az órarendi elemeit egyszerre szeretné megmutatni. Erőforrásokra szervezett API-nál ez több kérés lehet, vagy a szerver olyan reprezentációt ad, amely mindent tartalmaz (akár feleslegesen sokat is). Más helyzetben a kliens nem adatot kér, hanem műveletet: „indítsd el a jelentkezést”. A GraphQL az első, az RPC a második igényre ad természetes választ. Nem a REST „fejlettebb” vagy „elavult” változatai, hanem más hangsúlyú minták.

flowchart LR
    C[Kliens igénye] --> R[REST: erőforrás és reprezentáció]
    C --> G[GraphQL: mezőválasztás a sémában]
    C --> P[RPC: megnevezett művelet]

GraphQL: a kívánt mezők megnevezése

A GraphQL szolgáltatás sémája leírja, milyen típusok és mezők kérhetők. A kliens lekérdezése kiválasztja a szükséges mezőket, és a válasz pontosan ezt a szerkezetet követi. A GraphQL saját leírása szerint a kliens pontosan azt az adatot kérheti, amire szüksége van, egy erős típusrendszeren keresztül.

Egy kipróbált példa (a graphql JavaScript-könyvtár 17.0.2-es verziójával, memóriabeli adattal):

type Oktato { nev: String! }
type Kurzus { id: ID!, cim: String!, ferohely: Int!, oktato: Oktato }
type Query { kurzus(id: ID!): Kurzus }

A séma szerint van egy kurzus lekérdezés, amelynek id kell. Három kérés és a tényleges válasz:

{ kurzus(id: "webprog") { cim oktato { nev } } }
-> {"data":{"kurzus":{"cim":"Webprogramozás 1.","oktato":{"nev":"Minta Péter"}}}}

{ kurzus(id: "webprog") { cim ferohely } }
-> {"data":{"kurzus":{"cim":"Webprogramozás 1.","ferohely":40}}}

{ kurzus(id: "webprog") { cim szinpad } }
-> {"errors":[{"message":"Cannot query field \"szinpad\" on type \"Kurzus\".", ...}]}
  • Az első és a második kérés ugyanarra az erőforrásra vonatkozik, de más mezőket kér, és csak azokat kapja meg.
  • A harmadik kérés olyan mezőt kér, ami nincs a sémában: a szerver hibát ad, mielőtt bármit végrehajtana. A séma tehát a szerződés része: a kliens csak a támogatott mezőket kérheti.

A GraphQL nem azt jelenti, hogy a kliens közvetlenül az adatbázisban futtat tetszőleges lekérdezést: a szerver ellenőrzi és végrehajtja a kérést, saját szabályai szerint. A mezőválasztás akkor segíthet, ha sokféle kliens eltérő adatigénnyel dolgozik. Másfelől a séma, a jogosultság, a teljesítmény és a hibakezelés tervezést igényel. Az egyetlen végpontból érkező válasz nem automatikusan gyorsabb vagy egyszerűbb minden REST-erőforrásnál.

RPC: műveletek a középpontban

Az RPC (Remote Procedure Call, távoli eljáráshívás) azt a szemléletet emeli ki, hogy a kliens egy távoli, névvel jelölt műveletet kér. A jelentkezesInditasa fogalmilag művelet, nem egy erőforrás reprezentációjának lekérése. Az RPC lehet HTTP-n működő webes API, de maga az elv nem kötődik egyetlen adatformátumhoz vagy protokollhoz.

A JSON-RPC 2.0 egy egyszerű, nyilvános szabvány erre: a kérés tartalmazza a jsonrpc verziót, a method nevét, a params paramétereket és az id azonosítót; a válasz vagy result, vagy error mezőt hordoz, ugyanazzal az id-val. Kipróbálva (a kipróbáló szerver egyetlen műveletet ismer):

-> {"jsonrpc":"2.0","method":"jelentkezesInditasa","params":{"hallgato":"h42","kurzus":"webprog"},"id":1}
<- {"jsonrpc":"2.0","result":{"id":1,"ujLetrejott":true},"id":1}

-> (ugyanaz a hívás újra, id: 2)
<- {"jsonrpc":"2.0","result":{"id":1,"ujLetrejott":false},"id":2}

-> {"jsonrpc":"2.0","method":"mindentTorol","id":3}
<- {"jsonrpc":"2.0","error":{"code":-32601,"message":"Method not found"},"id":3}

Három megfigyelés:

  • Az első hívás létrehozta a jelentkezést, a második hívás ugyanezzel a hallgatóval és kurzussal nem hozott létre újat: a szerver felismerte, hogy már létezik. Ez a védekezés a duplikált jelentkezés ellen; az RPC nem mentesít az alól, hogy a szerver az ismételt hívást kezelje (2.3).
  • A válasz az id alapján párosítható a kéréshez.
  • Az ismeretlen művelet hibát kap: a JSON-RPC szabvány szerint a -32601 kód jelentése „Method not found”.

(A JSON-RPC-ben a hiba a válasz törzsében van; a HTTP-státuszkód ettől függetlenül lehet 200. Ez eltér a REST-től, ahol a státuszkód közvetlenül jelzi az eredményt.)

Hogyan hasonlítsuk össze őket?

REST GraphQL RPC
Kiindulópont erőforrás és HTTP-művelet séma és mezőválasztás megnevezett művelet
Mit kér a kliens? egy erőforrás reprezentációját pontosan megnevezett mezőket egy művelet végrehajtását
Hol van a szerződés? végpontok, metódusok, státuszkódok a séma a műveletek és paramétereik
Jó választás, ha... erőforrásokra jól leképezhető a feladat sokféle kliens eltérő adatigénnyel dolgozik a feladat célzott művelet

Valós rendszerek vegyes megoldásokat is alkalmaznak. Egy könyvtári katalógus jól illik az erőforrásos modellhez, egy sokféle felületen megjelenő összetett adat GraphQL-lel is jól kezelhető, egy célzott alkalmazási művelet pedig RPC-ként lehet világos. A technológia nevénél fontosabb, hogy a szerződés érthető és a kliens igényéhez illő legyen.

Végigvezetett példa: a Kurzuspont mobilkijelzője

A mobilkijelzőn a kurzus címe, az oktatója és a szabad helyek száma kell, az órarend viszont nem.

  • REST: GET /api/kurzusok/webprog a teljes kurzusobjektumot adja, az oktató külön kérés lehet, vagy a szerver beágyazza. Egyszerű, gyorsítótárazható.
  • GraphQL: a mobil csak a három kért mezőt kapja, egyetlen kérésben. A séma jól dokumentált, de a szervernek kezelnie kell a lekérdezések bonyolultságát.
  • RPC: kurzusAttekintes művelet egy előre összeállított nézetet ad. Nagyon célzott, de a művelet és a kliens szorosabban kötődik.

A Kurzuspont a kurzusadatokat REST-tel, a jelentkezést pedig akár célzott művelettel is kiszolgálhatja. A választás a feladat és a szerződés minőségének kérdése.

Gyakori tévhitek

Állítás Pontosítás
„GraphQL-ben a kliens közvetlenül az adatbázist olvassa.” A kliens a szolgáltatás sémáján keresztül kér adatot; a szerver ellenőrzi.
„Az RPC egy konkrét protokoll neve.” Műveletközpontú szemlélet; a JSON-RPC egy konkrét szabvány.
„A GraphQL mindig egyetlen gyors kérés.” A szerver munkája és az adatigény összetettsége továbbra is számít.
„Egyetlen stílus minden API-nál helyes.” A feladat és a szerződés minősége dönt.
„RPC-nél nem kell az ismétlésre gondolni.” A hívás megismétlése itt is kezelendő (idempotencia, 2.3).

Röviden

  • A GraphQL sémából mezőket választ, a válasz a kérés szerkezetét követi.
  • Az RPC névvel jelölt műveletet hív; a JSON-RPC egy egyszerű, szabványos forma.
  • A séma, illetve a műveletek leírása a szerződés.
  • A választás a kliens igényétől és a szerződés érthetőségétől függ, nem divattól.

Ellenőrző kérdések

  1. Mit nyer a kliens azzal, hogy a GraphQL-ben a mezőket maga választja ki?
  2. Mi történik, ha a lekérdezés olyan mezőt kér, ami nincs a sémában?
  3. Miért kell az RPC-ben is gondolni az ismételt hívásra?
  4. Mikor választanál RPC-t REST helyett?

Tanulási források

Prerequisites (2)

  • webes API
  • JSON

Teaches (2)

  • GraphQL
  • RPC