Utilizarea API-urilor a cunoscut o creștere extraordinară. Pe măsură ce aplicațiile mobile și web au devenit mai populare, a existat o nevoie evidentă de a deconecta backendul aplicației de la front-end. Adevărul este că API-urile sunt acum prezente în majoritatea software-urilor dezvoltate în zilele noastre. Această utilizare pe scară largă a API-urilor a arătat, de asemenea, câteva dezavantaje ale modului în care API-urile au fost construite și consumate, ceea ce a generat inovații precum REST și GraphQL. În cadrul companiei noastre avem proiecte cu GraphQL, unde implementarea a avut succes și nu au fost showstoppers din punct de vedere tehnic.
GraphQL este, într-adevăr, tehnologia care a atras atenția comunității de developeri din acest moment. Unii dezvoltatori chiar o descriu ca fiind "a better Rest". De ce? Câteva dintre argumentele principale ar fi: un singur request pentru a aduce date de pe server comparativ cu requesturi separate pentru fiecare resursă; un singur endpoint pentru toate requesturile comparativ cu fiecare resursă având endpointul propriu, plus multe altele pe care le vom aborda puțin mai târziu.
Aceste argumente stau la baza GraphQL-ului, care a devenit un standard pentru dezvoltarea de API și care a câștigat încrederea din ce în ce mai multor developeri.
În ultimii ani, GraphQL a dovedit că își menține puterea, proiectele, produsele și serviciile, crescând pentru a forma un ecosistem în jurul său.
Figura 1. Evoluția GraphQL . Sursa: Google Trends
Definiția ca la carte spune că GraphQL este o specificație a unui limbaj pentru API-uri, prin care poți să interoghezi și să manipulezi date.
Principalul concept care stă la baza GraphQL este faptul că descrie datele disponibile prin intermediul unei scheme. În consecință, consumatorul de API poate cere exact ceea ce are nevoie. Mai multe informații despre nomenclatura GraphQL, puțin mai jos.
A fost conceput de Facebook în anul 2012, dar a fost făcut public doar din 2015, iar prima versiune stabilă a ieșit pe piața doar în iunie 2018.
Este open-source dezvoltat cu JavaScript, Ruby, Scala și din ce în ce mai multe companii au început să îl utilizeze în dezvoltarea de produse software.
Pentru a înțelege mai bine conceptele GraphQL, mai jos, sunt explicate câteva dintre cuvintele cheie:
Query: Este operația de citire a datelor solicitată unui server GraphQL.
Mutation: Este operația de scriere (create, update, delete) a datelor solicitată unui server GraphQL.
Subscription: Este operația prin care se obțin actualizări în timp real de pe serverul GraphQL. În calitate de queries, subscriptions vă permit să preluați date. Spre deosebire de queries, subscriptions mențin o conexiune activă la serverul GraphQL. Acest lucru permite serverului să trimită actualizări ale rezultatului unui subscription în timp real.
Resolver: În GraphQL, Resolver este responsabil pentru maparea dintre operațiunea cerută de client și codul care rulează pe backend. Sunt utilizate pentru a oferi instrucțiuni pentru conversia operației GraphQL în date. De asemenea, Resolver separă schema bazei de date de schema API, ceea ce face ușoară modificarea conținutului obținut din baza de date. Se poate spune că Resolverul în GraphQL este similar cu backendul MVC dintr-o aplicație RESTFul .
Type: Definește forma datelor de răspuns care pot fi returnate de pe serverul GraphQL.
Input: Definește forma datelor de intrare care sunt trimise către un server.
Scalar: Este un "primitive type", cum ar fi: String, Int, Boolean, Float etc.
Un server GraphQL oferă unui client o schemă predefinită - un model al datelor care pot fi solicitate de la server. Cu alte cuvinte, schema servește ca un punct de mijloc între client și server în timp ce definește modul de accesare a datelor.
Figura 2. Schema generală a arhitecturii de GraphQL
Prima dată când Facebook a vorbit public despre GraphQL a fost la React.js Conf 2015. Deoarece Facebook obișnuia să vorbească întotdeauna despre GraphQL în contextul React, a durat ceva timp ca developerii non-React să înțeleagă că GraphQL nu este în niciun caz o tehnologie care se limitează la utilizarea cu React.
GraphQL este un limbaj de programare agnostic și există diferite implementări în multe limbaje (Java, C#, PHP, Scala, etc).
În acest articol ne axăm mai mult pe limbajul Java, deci alegerea firească de implementare este graphql-java. Este posibil să nu fie singura implementare Java, dar este cu siguranță cea mai folosită și matură în acest moment. Acest proiect de bază, graphql-java, rămâne fidel specificațiilor, dar există o serie de alte proiecte de sprijin pentru a facilita integrarea și utilizarea acestuia. Mai multe detalii despre celelalte proiecte, se găsesc aici.
În GraphQL, trebuie declarată schema de date, astfel încât consumatorii de API să poată introspecta sistemul și să interogheze corect datele. Cu toate acestea, schema trebuie să fie o reprezentare exactă a claselor de date. Deși s-ar putea crea ambele lucruri manual, generarea uneia din cealaltă va reduce efortul și erorile. O abordare este definirea schemei și generarea claselor de date pe baza schemei. Această abordare este furnizată de graphql-apigen și graphql-java-tools.
Cu toate că această abordare ar putea fi foarte utilă dacă este un proiect nou cu o specificație strictă a schemei, ce se poate face când avem deja clasele definite? Pentru a rezolva această situație, există un proiect numit graphql-java-annotations care se bazează pe această abordare. Totuși, dezvoltarea acestuia pare să se fi oprit și comunitatea pare să se îndrepte spre graphgql-spqr (pronunțat "speaker"). Se pare că aceasta va deveni abordarea oficială a clasei graphql-java.
Pentru a evidenția mai bine cum se folosesc aceste librării, vom construi un mic exemplu cu Spring Boot, Hibernate(Mysql) și Maven. Astfel, se adaugă dependințele de GraphQL în pom.xml:
graphql-spring-boot-starter este utilizat pentru activarea servletului GraphQL. Inițializează beanul GraphQLSchema.
graphql-java ne permite să scriem schema cu limbajul schemei GraphQL.
După ce se construiesc entitățile și clasele de repository, următorul pas ar fi definirea schemei în folderul de resources, într-un fişier(e) cu extensia ". graphqls":
type Vehicle {
id: ID!,
type: String,
modelCode: String,
brandName: String,
launchDate: String
}
type Query {
vehicles(count: Int): [Vehicle]
vehicle(id: ID):Vehicle
}
type Mutation {
createVehicle(type: String!,
modelCode: String!,
brandName: String,
launchDate: String):Vehicle
Obiectele de query sau mutation nu au nicio clasă de date asociată. De aceea, este nevoie de clasele Resolver care implementează GraphQLQueryResolver /GraphQLMutationResolver:
package com.demo.graphql.mutation;
import org.springframework.beans.factory
.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.coxautodev.graphql
.tools.GraphQLMutationResolver;
import com.demo.graphql.dao.entity.Vehicle;
import com.demo.graphql.service.VehicleService;
@Component
public class VehicleMutation implements
GraphQLMutationResolver {
@Autowired
private VehicleService vehicleService;
public Vehicle createVehicle(final String type,
final String modelCode, final String brandName){
return this.vehicleService.createVehicle(type,
modelCode, brandName, launchDate);
}
}
și
package com.demo.graphql.query;
...
@Component
public class VehicleQuery
implements GraphQLQueryResolver {
@Autowired
private VehicleService vehicleService;
public List getVehicles(final int count) {
return this.vehicleService.getAllVehicles(count);
}
public Optional getVehicle(final int id) {
return this.vehicleService.getVehicle(id);
}
}
Se poate observa că semnăturile metodelor din clasele Resolver pentru query și mutation au fost declarate și în schema de mai sus. Pentru mai multe detalii despre implementarea mini exemplului, găsiți pe GitHub.
În partea de testare și debugging a API-ului, GraphQL vine în ajutorul developerilor și oferă un mediu de dezvoltare integrat (IDE) care maximizează productivitatea.
Cei de la GraphQL se axează mult pe partea de web. Ca urmare, alegerea lor firească pentru un IDE este rularea lui în browser care permite vizualizarea schemei, scrierea de queries/mutations și afișarea rezultatelor. Sunt câteva implementări precum: GraphQL Playground, GraphQL IDE dar cea mai folosită implementare se numește GraphiQL.
Bineînțeles că sunt și alte opțiuni care pot fi luate în calcul: curl, wget, tool-uri(Postman) care reduc "the boilerplate" și îmbunătățesc portabilitatea, dar ignoră semantica datelor. De exemplu, dacă se trimite un tip greșit de argument sau doar pentru verificarea de ce opțiuni sunt disponibile, trebuie să vă bazați pe API pentru a fi descriptiv și util. Toate aceste probleme sunt rezolvate de GraphiQL pentru că acceptă debugging as-you-type, oferind sugestii și subliniind erorile. Totodată, GraphiQL vine cu un vizualizator JSON cu toate caracteristicile aferente: pliere cod, indentare automată etc.
După rularea mini exemplului nostru cu Spring, se poate accesa graphiQL la calea: http://localhost:8080/graphiql
și va redirecta userul către interfața unde se pot rula queries, mutations și introspecta schema.
Ca orice din lumea tehnică, și GraphQL are parți pozitive dar și parți negative. Cum mulți spun că GraphQL ar putea fi sfârșitul lui Rest, pentru a putea evidenția mai bine punctele forte și cele slabe ale GraphQL-ului, în rândurile următoare expunem o mică analiză comparativă a celor două.
Deoarece ambele sunt specificații pentru construirea și consumarea API-urilor, GraphQL și REST au lucruri în comun. Ambii recuperează resurse prin trimiterea de interogări, pot returna date JSON și pot fi operați prin HTTP.
În plus, vom vorbi despre diferența GraphQL vs.REST și despre modul în care schimbă experiența dezvoltatorului de a construi și consuma un API.
Soluția perfectă pentru sisteme complexe și microservicii. Prin integrarea mai multor sisteme în spatele API-ului său, GraphQL le unifică și le ascunde complexitatea. Serverul GraphQL este apoi responsabil pentru preluarea datelor din sistemele existente și împachetarea acestora în formatul de răspuns GraphQL. Acest lucru ajută foarte mult când infrastructurile vechi sau API-urile terțe care s-au extins de-a lungul anilor și mentenanța lor sunt destul de dificile.
O altă situație în care se pretează soluția cu GraphQL ar fi migrarea de la o aplicație backend monolitică la o arhitectură de microservicii. Un API GraphQL poate ajuta la gestionarea comunicării între mai multe microservicii prin fuzionarea acestora într-o singură schemă GraphQL. În timp ce fiecare microserviciu își definește propria schemă GraphQL și are propriul punct final GraphQL, un gateway API GraphQL consolidează toate schemele într-o singură schemă globală.
Validarea automată a datelor. Funcția de introspecție a GraphQL permite navigarea în types și descoperirea schemei pentru a se asigura că aplicațiile cer doar ceea ce este posibil și în formatul dictat de schemă. Astfel, se poate interoga schema și se poate observa cum sunt configurate datele acolo. Pe baza acestora, se pot adăuga cu ușurință câmpuri noi la interogările existente printr-un IDE GraphQL.
Figura 4. Gateway API GraphQL. Sursa: How to GraphQL
Versionarea API-ului. Dezvoltarea API implică o problemă de a păstra vechea versiune până când dezvoltatorii fac trecerea la cea nouă. Prin urmare, cu REST este obișnuit să oferiți mai multe versiuni API. Cu toate acestea, GraphQL elimină necesitatea versionării prin deprecated API la nivel de câmp. Câmpurile deprecated pot fi ulterior eliminate din schemă fără a afecta queries existente. GraphQL permite acest lucru prin crearea unui API uniform pe întreaga aplicație care nu este limitat de un anumit motor de stocare.
Code sharing. În GraphQL, câmpurile utilizate în mai multe queries pot fi partajate la un nivel mai ridicat de componentă pentru reutilizare. Acest lucru se poate realiza prin intermediul unei caracteristici GraphQL numită fragments. Astfel, se pot obține date diferite păstrând același field schema.
Mesaje de eroare. GraphQL pune accent pe consistență și predictibilitate și, pe acel ton, răspunsul de la un server GraphQL are întotdeauna o structură previzibilă, constând din cele 3 câmpuri:
Câmpul "data" - unde este stocat rezultatul operației;
Câmpul de "errors" - unde sunt păstrate toate erorile acumulate în timpul executării operației;
Orice server GraphQL va gestiona automat erorile sintactice și de validare și va informa clientul în mod corespunzător, dar excepțiile întâlnite în funcțiile de rezolvare necesită de obicei o manipulare specifică aplicației. Gestionarea erorilor poate fi personalizată la câteva niveluri diferite.
La cel mai înalt nivel, graphql-java-servlet expune o metodă (numită isClientError) care decide dacă mesajul unei erori va fi trimis clientului textual sau dacă va fi ascuns de un mesaj de eroare generic al serverului. În mod implicit, numai erorile sintactice și de validare vor fi trimise așa cum sunt. Aceasta este o valoare implicită rezonabilă, deoarece mesajele de excepție pot dezvălui o mulțime de informații care, în mod normal, ar trebui ascunse vizualizării publice. Cu toate acestea, mesajele de eroare neinformative (sau chiar prea multe mesaje) pot avea un impact negativ sever asupra utilizabilității API-ului.
Figura 5. Structura Response - Eroare aruncata de GraphQL server backend
Un răspuns GraphQL are întotdeauna statusul HTTP 200 OK.
Codurile de eroare HTTP nu sunt relevante atunci când se utilizează GraphQL deoarece, dacă o solicitare eșuează, sarcina utilă JSON a răspunsului serverului va conține un câmp rădăcină numit "errors" care adună informații precise despre problemele apărute pe partea serverului. Mai mult, deoarece GraphQL permite trimiterea mai multor operațiuni în aceeași solicitare, este foarte posibil ca o cerere să nu reușească decât parțial și să returneze date și erori reale.
Probleme de performanță cu interogări complexe. În timp ce permite clienților să solicite exact ceea ce au nevoie, GraphQL query poate întâmpina probleme de performanță dacă un client solicită prea multe câmpuri imbricate, respectiv un response Json foarte complicat.
Suprasolicitare pentru aplicații mici. În timp ce GraphQL este soluția potrivită pentru mai multe microservicii, este mai bine să alegeți arhitectura REST în cazul unei aplicații simple. REST poate fi, de asemenea, o abordare valoroasă pentru conectarea aplicațiilor bazate pe resurse care nu au nevoie de interogările flexibile oferite de GraphQL.
Cache web. Memorarea în cache a GraphQL la baza de date sau client poate fi implementată cu clienții Apollo sau Relay care au mecanisme de cache încorporate. Cu toate acestea, GraphQL nu se bazează pe metodele de cache HTTP, care permit stocarea conținutului unei cereri. Memorarea în cache ajută la reducerea cantității de trafic către un server, păstrând informații accesate frecvent de către client.
Upload de fișiere. Deoarece GraphQL nu suportă obiecte de tip fișiere, o caracteristică de încărcare a fișierelor nu este inclusă în specificația sa. Pentru a permite încărcarea fișierelor în aplicații web cu GraphQL, există mai multe opțiuni: codificare în Base64 a conținutului de fișier sau folosirea unei biblioteci care suportă multipart request.
Cele mai potrivite scenarii pentru utilizarea GraphQL se pretează cel mai bine sunt:
Aplicații pentru dispozitive precum telefoane mobile, ceasuri inteligente și dispozitive IoT.
Aplicațiile în care datele imbricate trebuie preluate într-un singur apel. De exemplu, un blog sau o platformă de rețea socială în care postările trebuie preluate împreună cu comentarii imbricate și detalii ale comentatorilor.
Figura 6. GraphQL server - agregare de resurse multiple. Sursa: Technology Radar
Într-un discurs la Summitul GraphQL din 2016, Lee Byron (Executive Director, GraphQL Foundation, și GraphQL Co-Creator) a prezentat un "Master Plan secret" în care își proiectează evoluția GraphQL.
Într-o lume ideală, spunea el, adoptarea GraphQL va arăta cam așa:
"1-3 luni - Hobbyiști și proiecte personale;
6 luni - Implementat în peste 3 limbi;
9-12 luni - Startup-uri noi și companii mici;
1,5-2 ani - Companii și produse de dimensiuni medii;
2 ani - Implementat în peste 10 limbi (de fapt a durat ~ 3 luni)
2-4 ani - Companii mari și giganți tehnologici;
În acest moment, ne aflăm pe la sfârșitul fazei companiilor mari și a giganților tehnologici - GitHub, Pinterest, Intuit, Coursera și Shopify sau alte companii, care folosesc toate, GraphQL într-un mediu sau altul.
În timp ce GraphQL s-ar putea să nu sune perfect pentru REST, acesta conține setul potrivit de ingrediente de a fi în jur pentru mult timp. Ca urmare, nu este dificil să ne imaginăm că planul de 4-5 ani pe care l-a menționat Byron se desfășoară așa cum spera, deși poate că este un pic prea ambițios din punct de vedere al omniprezenței.
De asemenea, Alan Johnson spunea într-un articol al său că GraphQL va fi noul trend în dezvoltarea de API și că în câțiva ani REST va deveni demodat: "Mark my words: in 5 years, newly minted full-stack app developers won't be debating RESTfulness anymore, because REST API design will be obsolete. "
După părerea mea, atât REST, cât și GraphQL sunt modalități eficiente de a proiecta modul în care va funcționa un API și modul în care aplicațiile vor accesa datele din acesta. Oricare dintre cele două sunt soluții excelente. Alegerea uneia dintre ele ar trebui să revină echipei de dezvoltare și să se facă în urma unui studiu de caz care să pună în balanța avantajele și dezavantajele fiecăruia dintre cele două standarde.
Eve Porcello and Alex Banks(2018). Learning GraphQL
Technology Radar - GraphQL for server-side resource aggregation