Ukratko

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
  1. Razumijevanje GraphQL-a: Sveobuhvatni Vodič za Dizajn i Implementaciju API-ja
  2. Što je GraphQL i Zašto ga Koristiti?
  3. Ključne Prednosti GraphQL-a
  4. Usporedba s REST-om
  5. Temeljni Koncepti GraphQL-a
  6. Shema (Schema)
  7. Tipovi podataka
  8. Upiti (Queries)
  9. Mutacije (Mutations)
  10. Resolveri (Resolvers)
  11. Dizajniranje i Implementacija GraphQL Servera
  12. 1. Definiranje Sheme s SDL-om
  13. 2. Pisanje Resolvera
  14. 3. Postavljanje GraphQL Servera
  15. 4. Autentikacija i Autorizacija
  16. Korištenje GraphQL API-ja s Klijentima
  17. Klijentske Biblioteke
  18. Primjer s Apollo Client-om (React)
  19. Alati za Razvoj
  20. Napredne Teme i Best Practices
  21. 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 Query definira sve dostupne upite koje klijent može poslati (npr. dohvaćanje korisnika ili postova).
  • type Mutation definira sve operacije koje mijenjaju podatke (npr. kreiranje, ažuriranje, brisanje).
  • User i Post su 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: true ili false.

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): context objekt 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 Authorization zaglavlju i parsiraju u context objektu 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. @deprecated za zastarjela polja ili @auth za 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 errors polja 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.

Izvori i dodatno čitanje

  1. GraphQL Službena Dokumentacija
  2. Apollo Server Dokumentacija
  3. GraphQL by Example (Howtographql.com)
  4. Fullstack GraphQL knjiga
B
Uredništvo portala

BAJT

Službeni autorski profil redakcije portala BAJT. Sadržaj priprema i provjerava uredništvo portala.