Najvažnije iz članka
- GraphQL je jezik za upite i runtime za API-je koji rješava probleme prekomjernog/nedovoljnog dohvaćanja podataka, omogućujući klijentima da deklarativno specificiraju točno koje podatke trebaju u jednom zahtjevu.
- Ključni koncepti uključuju shemu (definira tipove podataka), upite (za dohvaćanje podataka), mutacije (za mijenjanje podataka) i resolvere (funkcije koje implementiraju logiku dohvaćanja/mijenjanja podataka za polja u shemi).
- Implementacija GraphQL servera obuhvaća definiranje sheme pomoću SDL-a, pisanje robustnih resolvera koji komuniciraju s izvorima podataka te postavljanje servera pomoću biblioteka poput Apollo Servera.
- Klijentske biblioteke (npr. Apollo Client) pojednostavljuju integraciju s GraphQL API-jem, nudeći keširanje, upravljanje stanjem i optimizirano dohvaćanje podataka za bolje performanse aplikacije.
Sadržaj članka
- Razumijevanje GraphQL-a: Sveobuhvatni Vodič za Dizajn i Implementaciju API-ja
- Što je GraphQL i Zašto ga Koristiti?
- Ključne Prednosti GraphQL-a
- Usporedba s REST-om
- Temeljni Koncepti GraphQL-a
- Shema (Schema)
- Tipovi podataka
- Upiti (Queries)
- Mutacije (Mutations)
- Resolveri (Resolvers)
- Dizajniranje i Implementacija GraphQL Servera
- 1. Definiranje Sheme s SDL-om
- 2. Pisanje Resolvera
- 3. Postavljanje GraphQL Servera
- 4. Autentikacija i Autorizacija
- Korištenje GraphQL API-ja s Klijentima
- Klijentske Biblioteke
- Primjer s Apollo Client-om (React)
- Alati za Razvoj
- Napredne Teme i Best Practices
- Zaključak
Razumijevanje GraphQL-a: Sveobuhvatni Vodič za Dizajn i Implementaciju API-ja
U današnjem svijetu web razvoja, efikasno dohvaćanje podataka je ključno za performanse i skalabilnost aplikacija. Tradicionalni REST API-ji, iako široko rasprostranjeni, često pate od problema prekomjernog dohvaćanja (overfetching) ili nedovoljnog dohvaćanja (underfetching) podataka. Upravo ovdje na scenu stupa GraphQL, osiguravajući fleksibilniji i optimiziraniji pristup dohvaćanju i manipulaciji podacima. Ovaj vodič će vas provesti kroz osnove GraphQL-a, od dizajna shema do implementacije klijenata, pružajući dubinski uvid u ovu moćnu tehnologiju.
Što je GraphQL i Zašto ga Koristiti?
GraphQL je jezik za upite (query language) za vaše API-je i runtime za izvršavanje tih upita s postojećim podacima. Razvijen od strane Facebooka 2012. godine, a otvorenog koda od 2015., GraphQL je brzo stekao popularnost zbog svoje sposobnosti da rješava uobičajene probleme s kojima se developeri susreću pri korištenju REST-a.
Ključne Prednosti GraphQL-a
- Efikasno dohvaćanje podataka: Klijent točno specificira koje podatke treba, eliminirajući overfetching i underfetching. To rezultira manjim prijenosom podataka i bržim učitavanjem aplikacija.
- Jedna endpoint točka: Za razliku od REST-a gdje imate više endpointa (npr.
/users,/products/123), GraphQL obično izlaže jednu endpoint točku (/graphql) preko koje se obavljaju svi upiti. - Fleksibilnost: Klijenti mogu dinamički mijenjati strukturu zahtjeva bez potrebe za promjenama na serverskoj strani.
- Samo-dokumentacija: GraphQL shema služi kao centralni izvor istine za vaš API, pružajući ugrađenu dokumentaciju koja je uvijek ažurna.
- Unaprijeđeno iskustvo developera: Klijentske biblioteke kao što su Apollo Client ili Relay olakšavaju rad s GraphQL-om, nudeći napredne značajke poput keširanja i upravljanja stanjem.
Usporedba s REST-om
Dok REST izlaže resurse kao odvojene URL-ove i koristi HTTP metode (GET, POST, PUT, DELETE) za interakciju s njima, GraphQL se fokusira na deklarativno dohvaćanje podataka putem upita. S REST-om, dohvaćanje povezanih podataka često zahtijeva više HTTP zahtjeva (n+1 problem). GraphQL rješava ovo tako da klijent može zahtijevati sve potrebne podatke u jednom upitu, smanjujući latenciju i mrežni promet.
Temeljni Koncepti GraphQL-a
Prije nego što zaronimo u implementaciju, ključno je razumjeti osnovne koncepte GraphQL-a.
Shema (Schema)
Srce svakog GraphQL API-ja je njegova shema. Shema definira tipove podataka koje možete upitati i mutirati, kao i odnose između tih tipova. Piše se pomoću GraphQL Schema Definition Language (SDL).
type Query {
hello: String
user(id: ID!): User
users: [User]
}
type User {
id: ID!
name: String!
email: String
posts: [Post]
}
type Post {
id: ID!
title: String!
content: String
author: User!
}
type Mutation {
createUser(name: String!, email: String): User
updateUser(id: ID!, name: String, email: String): User
createPost(title: String!, content: String, authorId: ID!): Post
}
U ovom primjeru:
type Querydefinira sve dostupne upite koje klijent može poslati (npr. dohvaćanje korisnika ili postova).type Mutationdefinira sve operacije koje mijenjaju podatke (npr. kreiranje, ažuriranje, brisanje).UseriPostsu objektni tipovi (Object Types) koji grupiraju polja (fields).ID!,String!,[User]su skalarni tipovi (Scalar Types) i listovi (Lists). Uskličnik (!) označava da je polje obavezno (non-nullable).
Tipovi podataka
GraphQL ima nekoliko ugrađenih skalarnih tipova:
ID: Jedinstveni identifikator, često string, ali označen kao ID.String: Tekstualni podaci.Int: Cijeli brojevi.Float: Brojevi s pomičnim zarezom.Boolean:trueilifalse.
Možete definirati i enum tipove (Enumeration Types), input tipove (Input Types) za mutacije, te interface tipove (Interface Types) i union tipove (Union Types) za apstraktnije sheme.
Upiti (Queries)
Upiti se koriste za dohvaćanje podataka. Klijent specificira točno polja koja želi dobiti.
query GetUserAndPosts($userId: ID!) {
user(id: $userId) {
id
name
email
posts {
id
title
content
}
}
}
Ovaj upit traži korisnika s određenim id-jem i za tog korisnika dohvaća id, name, email te id, title i content svih njegovih postova.
Mutacije (Mutations)
Mutacije se koriste za promjenu podataka na serveru (kreiranje, ažuriranje, brisanje).
mutation CreateNewUser($name: String!, $email: String) {
createUser(name: $name, email: $email) {
id
name
email
}
}
Ovdje kreiramo novog korisnika i natrag tražimo id, name i email kreiranog korisnika.
Resolveri (Resolvers)
Dok shema definira strukturu podataka, resolveri definiraju kako se podaci dohvaćaju ili mijenjaju. Svako polje u shemi ima svoj resolver (ili se nasljeđuje od roditeljskog polja). Resolver je funkcija koja je odgovorna za vraćanje podataka za to polje.
Primjer resolvera u JavaScriptu (koristeći popularnu biblioteku graphql-js i apollo-server):
const resolvers = {
Query: {
hello: () => 'Hello world!',
user: (parent, args, context, info) => {
// args sadrži argumente upita, npr. { id: '123' }
return context.dataSources.usersAPI.getUserById(args.id);
},
users: (parent, args, context, info) => {
return context.dataSources.usersAPI.getAllUsers();
},
},
User: {
posts: (parent, args, context, info) => {
// parent je objekt User koji dolazi iz prethodnog resolvera
return context.dataSources.postsAPI.getPostsByUserId(parent.id);
},
},
Mutation: {
createUser: (parent, args, context, info) => {
return context.dataSources.usersAPI.createUser(args.name, args.email);
},
},
};
Dizajniranje i Implementacija GraphQL Servera
Implementacija GraphQL servera obuhvaća nekoliko koraka.
1. Definiranje Sheme s SDL-om
Prvi korak je definirati vašu GraphQL shemu. Razmislite o entitetima u vašem sustavu i njihovim odnosima. Definirajte Query i Mutation tipove, te sve ostale objektne, input, enum, interface i union tipove koji su potrebni.
- Razmislite o klijentima: Koje podatke klijenti trebaju? Kako će ih koristiti?
- Granularnost: Budite precizni u definiranju polja. Preferirajte manja, specifična polja umjesto velikih, generičkih.
- Nomenklatura: Koristite jasna i konzistentna imena za tipove i polja. CamelCase je uobičajen.
2. Pisanje Resolvera
Nakon sheme, pišete resolver funkcije koje implementiraju logiku dohvaćanja i manipulacije podacima. Resolveri mogu komunicirati s bazama podataka, REST API-jima, drugim GraphQL API-jima, mikroservisima ili bilo kojim drugim izvorom podataka.
- Modularnost: Organizirajte resolvere modularno, po tipovima ili domenama, kako bi kod bio čitljiviji i lakši za održavanje.
- Kontekst (Context):
contextobjekt u resolveru je moćan alat. Omogućuje dijeljenje resursa (poput baza podataka, autentikacijskih podataka, data izvora) među svim resolverima unutar jednog zahtjeva. - Data Loaders (DataLoader): Za rješavanje N+1 problema unutar samog GraphQL servera (kada resolver dohvaća isti entitet više puta, npr. autora za svaki post), koristite DataLoader. On grupira zahtjeve za istim resursima i izvodi ih u jednom batch-u, značajno poboljšavajući performanse.
3. Postavljanje GraphQL Servera
Postoji mnogo biblioteka i frameworka za postavljanje GraphQL servera u različitim programskim jezicima. Popularni su:
- JavaScript/Node.js:
Apollo Server,graphql-yoga,express-graphql. - Python:
Graphene,Ariadne. - Java:
graphql-java,Netflix DGS. - Ruby:
graphql-ruby.
Na primjer, s Apollo Serverom u Node.js-u:
const { ApolloServer, gql } = require('apollo-server');
// 1. Definirajte shemu
const typeDefs = gql`
type Query {
hello: String
users: [User]
}
type User {
id: ID!
name: String!
email: String
}
type Mutation {
createUser(name: String!, email: String): User
}
`;
// Mock podaci za primjer
const users = [
{ id: '1', name: 'Alice', email: 'alice@example.com' },
{ id: '2', name: 'Bob', email: 'bob@example.com' },
];
// 2. Definirajte resolvere
const resolvers = {
Query: {
hello: () => 'Hello Bajt!',
users: () => users,
},
Mutation: {
createUser: (parent, { name, email }) => {
const newUser = { id: String(users.length + 1), name, email };
users.push(newUser);
return newUser;
},
},
};
// 3. Postavite Apollo Server
const server = new ApolloServer({ typeDefs, resolvers });
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
4. Autentikacija i Autorizacija
Autentikacija i autorizacija su ključni za sigurnost. U GraphQL-u se obično implementiraju na razini middlewarea (prije nego što zahtjev dođe do resolvera) ili unutar samih resolvera:
- Autentikacija: Provjera identiteta korisnika. Često se tokeni (JWT) šalju u HTTP
Authorizationzaglavlju i parsiraju ucontextobjektu za upotrebu u resolverima. - Autorizacija: Provjera dopuštenja korisnika za pristup određenim podacima ili izvršavanje operacija. Može se implementirati direktno u resolverima ili pomoću direktiva u shemi.
Korištenje GraphQL API-ja s Klijentima
Nakon što je GraphQL server aktivan, klijentske aplikacije mogu ga koristiti.
Klijentske Biblioteke
Za web aplikacije, popularne klijentske biblioteke uključuju:
- Apollo Client: Izuzetno popularan, pruža značajke kao što su keširanje, normalizacija podataka, upravljanje stanjem, optimistične UI ažuriranja i još mnogo toga. Integrira se s Reactom, Vueom, Angularom i drugim frameworkovima.
- Relay: Facebookova vlastita biblioteka, nudi performanse visokih razina, ali ima strmiju krivulju učenja i strože zahtjeve za GraphQL shemom.
Primjer s Apollo Client-om (React)
import React from 'react';
import { ApolloClient, InMemoryCache, ApolloProvider, gql, useQuery, useMutation } from '@apollo/client';
// Inicijalizacija Apollo Clienta
const client = new ApolloClient({
uri: 'http://localhost:4000/', // URL vašeg GraphQL servera
cache: new InMemoryCache(),
});
// GraphQL upit (query)
const GET_USERS = gql`
query GetUsers {
users {
id
name
email
}
}
`;
// GraphQL mutacija (mutation)
const CREATE_USER = gql`
mutation CreateUser($name: String!, $email: String) {
createUser(name: $name, email: $email) {
id
name
email
}
}
`;
function UsersList() {
const { loading, error, data } = useQuery(GET_USERS);
if (loading) return <p>Učitavanje korisnika...</p>;
if (error) return <p>Greška: {error.message}</p>;
return (
<div>
<h2>Korisnici:</h2>
<ul>
{data.users.map((user) => (
<li key={user.id}>{user.name} ({user.email})</li>
))}
</ul>
</div>
);
}
function CreateUserForm() {
const [name, setName] = React.useState('');
const [email, setEmail] = React.useState('');
const [createUser, { data, loading, error }] = useMutation(CREATE_USER, {
// Nakon uspješne mutacije, ponovno dohvati listu korisnika
refetchQueries: [{ query: GET_USERS }],
});
const handleSubmit = (e) => {
e.preventDefault();
createUser({ variables: { name, email } });
setName('');
setEmail('');
};
return (
<form
<h3>Kreiraj novog korisnika</h3>
<input
type="text"
placeholder="Ime"
value={name}
=> setName(e.target.value)}
/>
<input
type="email"
placeholder="Email"
value={email}
=> setEmail(e.target.value)}
/>
<button type="submit" disabled={loading}>Dodaj korisnika</button>
{error && <p>Greška pri kreiranju: {error.message}</p>}
{data && <p>Korisnik {data.createUser.name} kreiran!</p>}
</form>
);
}
function App() {
return (
<ApolloProvider client={client}>
<h1>Moj Bajt GraphQL Demo</h1>
<UsersList />
<CreateUserForm />
</ApolloProvider>
);
}
export default App;
Alati za Razvoj
- GraphQL Playground / GraphiQL: Interaktivna razvojna okruženja koja omogućuju pisanje i testiranje upita i mutacija, istraživanje sheme i pregled dokumentacije. Apollo Server ih često nudi u paketu.
- Extenzije za VS Code: Omogućuju automatsko dovršavanje (autocompletion), provjeru sintakse (linting) i formatiranje GraphQL koda.
Napredne Teme i Best Practices
- Fragmenti (Fragments): Omogućuju ponovno korištenje dijelova upita, smanjujući ponavljanje koda.
- Direktive (Directives): Dodaju metapodatke shemi ili izvršavaju logiku, npr.
@deprecatedza zastarjela polja ili@authza autorizaciju. - Keširanje: Klijentske biblioteke poput Apollo Clienta nude robustan sustav keširanja. Na serverskoj strani, možete implementirati keširanje na razini resolvera ili koristiti alate poput Redis-a.
- Paginacija (Pagination): Za velike skupove podataka, koristite paginaciju temeljenu na kursoru (cursor-based pagination) ili pomaku (offset-based pagination) kako biste efikasno dohvaćali dijelove podataka.
- Pretplata (Subscriptions): GraphQL podržava pretplate za real-time komunikaciju. Klijenti se mogu pretplatiti na događaje i primati ažuriranja u stvarnom vremenu (npr. nove poruke u chatu).
- Upravljanje Fehlerima: Definirajte dosljedan način vraćanja grešaka klijentu. GraphQL greške se vraćaju unutar
errorspolja u JSON odgovoru, ali možete i definirati specifične greške u shemi. - Verzioniranje: Prednost GraphQL-a je što često nije potrebno agresivno verzioniranje kao kod REST-a. Dodavanjem novih polja ili tipova u shemu postojećim klijentima neće smetati. Zastarjela polja se mogu označiti direktivom
@deprecated.
Zaključak
GraphQL predstavlja moćnu alternativu tradicionalnim REST API-jima, nudeći fleksibilnost, efikasnost i uvelike poboljšano iskustvo developera. Razumijevanje njegovih temeljnih koncepata – sheme, tipova, upita, mutacija i resolvera – ključno je za uspješnu implementaciju. Bilo da gradite novu aplikaciju ili modernizirate postojeću, GraphQL može značajno optimizirati dohvaćanje podataka i pojednostaviti klijentsku stranu vaše aplikacije. Njegova rastuća popularnost i snažna zajednica osiguravaju da je to tehnologija na koju vrijedi uložiti vrijeme.
Komentari