GraphQL i Apollo
Apollo Server, Apollo Client, URQL, DataLoader, Subscriptions, schema design i autoryzacja w TypeScript i Next.js.
6 GraphQL clients — porównanie
Apollo Client, URQL, TanStack Query, graphql-request, Relay i SWR — rozmiar, cache, mocne strony i optymalny use case.
| Klient | Rozmiar | Cache | Mocne strony | Kiedy |
|---|---|---|---|---|
| Apollo Client | ~46KB | Normalized (InMemoryCache) | Dojrzały, Federation, DevTools | Enterprise, complex caching, Federation |
| URQL | ~11KB | Document cache / graphcache | Lekki, exchanges, modular | Bundle-aware, custom caching |
| TanStack Query + graphql-request | ~7KB | TanStack Query cache | Familiar API, server-state | Migracja z REST, TanStack ecosystem |
| graphql-request | ~3KB | Brak (manual) | Minimalna, prosty fetch | Server-side, SSR, simple queries |
| Relay | ~70KB | Relay Store (normalized) | Meta battle-tested, Suspense, Connections | Large FB-scale apps, Relay spec |
| SWR + graphql-request | ~5KB | SWR cache | Stale-while-revalidate, Next.js | Next.js lightweight, simple queries |
Często zadawane pytania
Co to jest GraphQL i jak różni się od REST API?
GraphQL: query language + runtime dla API (Facebook/Meta, 2015). Jeden endpoint (/graphql). Klient określa co chce. Typy danych w Schema. REST API problems: Over-fetching (za dużo danych). Under-fetching (za mało, potrzeba wielu requestów). Wersjonowanie endpoint. GraphQL solutions: Query exactly what you need. Jeden request dla złożonych danych. Self-documenting (introspection). Schema as contract. GraphQL Schema (SDL): type User {id: ID!, email: String!, posts: [Post!]!}. type Post {id: ID!, title: String!, author: User!}. type Query {user(id: ID!): User. users: [User!]!}. type Mutation {createPost(title: String!): Post!}. type Subscription {onNewPost: Post!}. Resolver: function wypełniająca dane dla field. Query.user: (parent, args) => db.user.findUnique({where: {id: args.id}}). User.posts: (user) => db.post.findMany({where: {authorId: user.id}}). N+1 problem: każdy user -> osobny query dla posts. 1 query dla users + N queries dla posts każdego. DataLoader: batch + deduplicate. const userLoader = new DataLoader(ids => db.user.findMany({where: {id: {in: ids}}}). Automatyczne batching. Introspection: query {__schema {types {name}}}. GraphQL Playground/Explorer. Generowanie typów: graphql-codegen z schema -> TypeScript types. Fragmenty: reusable fields. fragment UserBasic on User {id email name}. Variables: query GetUser($id: ID!) {user(id: $id) {...UserBasic}}.
Apollo Server — GraphQL server w Node.js i Next.js?
Apollo Server 4 (2023): GraphQL server dla Node.js. Standalone lub z framework. @apollo/server: startStandaloneServer(server) lub expressMiddleware. Konfiguracja: const server = new ApolloServer({typeDefs, resolvers, context: ({req}) => ({user: req.user, db})}). Context: dostępny w każdym resolverze. Auth: sprawdź token w context. DataLoader: nowa instancja per request (w context). Schematics: typeDefs z gql tag lub .graphql files. Resolvers: Query, Mutation, Subscription, field resolvers. Error handling: throw new GraphQLError('Not found', {extensions: {code: 'NOT_FOUND'}}). error.extensions.code po stronie klienta. ApolloServer + Express: app.use('/graphql', expressMiddleware(server, {context: ...})). ApolloServer + Fastify: @as-integrations/fastify. Next.js API Route: import {ApolloServer} from '@apollo/server'. import {startServerAndCreateNextHandler} from '@as-integrations/next'. const handler = startServerAndCreateNextHandler(server). export {handler as GET, handler as POST}. Apollo Sandbox: wbudowany explorer dla dev. Introspection w dev, wyłączone w prod. Apollo Federation: mikroservisy z GraphQL. Subgraph per service. Gateway łączy. @federation directive. Potage: GraphQL za pomocą Pothos (TypeScript-first schema builder). Nexus: kolejny TypeScript-first builder. Code-first vs schema-first: SDL (schema first) -> resolvers. TypeScript classes (code first) -> schema generowane. Pothos, Nexus, TypeGraphQL (decorators).
Apollo Client vs URQL vs graphql-request — klientyside GraphQL?
Apollo Client: najpopularniejszy GraphQL client. In-memory cache. Normalized cache. Optimistic updates. Local state. React hooks: useQuery, useMutation, useSubscription. const {data, loading, error} = useQuery(GET_USER, {variables: {id}}). const [createPost, {loading}] = useMutation(CREATE_POST, {update(cache, {data}) {cache.modify(...)}}). Normalized cache: każdy obiekt cachowany per {__typename}:{id}. Aktualizacja jednego miejsca = aktualizacja wszędzie. ApolloProvider: wrappuj aplikację. Reactive variables: makeVar(). useReactiveVar() — local state przez Apollo. Apollo Client 3 DevTools. URQL: lekka alternatywa (Formidable, 2019). Document cache (default). Normalized cache (graphcache). Exchanges: fetchExchange, cacheExchange, authExchange, retryExchange, subscriptionExchange. const [result] = useQuery({query: GET_USER, variables: {id}}). const [result, executeMutation] = useMutation(CREATE_POST). URQL vs Apollo: URQL — mniejszy bundle, exchanges system, prostszy. Apollo — dojrzalszy, normalizowany cache, Federation. graphql-request: minimalna biblioteka. request(endpoint, query, variables). Bez cache. Bez hooks. Ideal dla: server-side fetching. SSR z TanStack Query: useQuery z queryFn: () => request(url, query). Cache zarządzany przez TanStack Query. Kiedy GraphQL: złożone relacje danych. Multiple clients (web + mobile). Self-documenting API. Real-time subscriptions. Kiedy REST: simple CRUD. External consumers. Caching standardowy (HTTP). Mniejszy overhead.
GraphQL Subscriptions — real-time z WebSocket i Server-Sent Events?
Subscriptions: real-time updates przez WebSocket. type Subscription {messageAdded(chatId: ID!): Message!}. Resolver: Async iterator lub PubSub. Apollo Server Subscriptions: @apollo/server + graphql-ws (WebSocket) lub graphql-sse (SSE). Server setup: const httpServer = createServer(app). const wsServer = new WebSocketServer({server: httpServer, path: '/graphql'}). WebSocketServer + ApolloServer. PubSub: in-memory (dev). graphql-redis-subscriptions — Redis (produkcja). const {PubSub} = graphql-subscriptions. pubSub.publish('MESSAGE_ADDED', {messageAdded: message}). pubSub.asyncIterator('MESSAGE_ADDED'). Resolver: Subscription: {messageAdded: {subscribe: withFilter(pubSub.asyncIterator, (payload, variables) => payload.chatId === variables.chatId)}}. Apollo Client subscriptions: import {split} from '@apollo/client'. import {GraphQLWsLink} from '@apollo/client/link/subscriptions'. createClient({url: 'ws://localhost/graphql'}). splitLink: HTTP dla queries/mutations, WS dla subscriptions. useSubscription hook: const {data} = useSubscription(ON_MESSAGE, {variables: {chatId}}). Server-Sent Events (SSE): alternatywa dla WebSocket. Jednostronne (server -> client). Prostsze. graphql-sse library. HTTP/2 kompatybilny. Proxy-friendly. Kiedy Subscriptions: live feeds, notifications, chat, collaborative editing. Alternatywa: polling (useQuery pollInterval: 5000). Webhook-to-client przez SSE. Pusher/Ably (managed).
Schema design, pagacja i autoryzacja w GraphQL?
Schema design principles: Nullable domyślnie (GraphQL best practice). Non-null (!) dla pewnych danych. ID type dla identyfikatorów. Enums dla stałych: enum Status {ACTIVE INACTIVE PENDING}. Input types dla mutations: input CreatePostInput {title: String! content: String! status: Status}. Cursor-based pagination (Relay spec): type PostConnection {edges: [PostEdge!]! pageInfo: PageInfo!}. type PostEdge {node: Post! cursor: String!}. type PageInfo {hasNextPage: Boolean! hasPreviousPage: Boolean! startCursor: String endCursor: String}. query Posts($first: Int $after: String) {posts(first: $first after: $after) {edges {node {id title}} pageInfo {hasNextPage endCursor}}}. Offset pagination (simpler): posts(offset: 0, limit: 10). Autoryzacja: Field-level auth w resolverach. Context: {user, db}. Query.adminData: (parent, args, ctx) => {if (!ctx.user.isAdmin) throw new GraphQLError('Forbidden', {extensions: {code: 'FORBIDDEN'}}). return ...}. graphql-shield: permissions middleware. rule(() => isAuthenticated()). shield({Query: {me: isAuthenticated, adminData: isAdmin}}). Directives: @auth, @deprecated, @skip, @include. Custom: @auth(role: ADMIN). Persisted queries: hash query po stronie serwera. Mniejszy payload. Security. Apollo persisted queries. Complexity limiting: graphql-cost-analysis. Depth limit. Rate limit per query complexity. Field-level extensions: @deprecated(reason: 'Use newField instead').
Powiązane artykuły
Skontaktuj się z nami
Porozmawiajmy o Twoim projekcie. Bezpłatna wycena w ciągu 24 godzin.
Wyślij zapytanie
Telefon
+48 790 814 814
Pon-Pt: 9:00 - 18:00
adam@fotz.pl
Odpowiadamy w ciągu 24h
Adres
Plac Wolności 16
61-739 Poznań
Godziny pracy
Wolisz porozmawiać?
Zadzwoń teraz i porozmawiaj z naszym specjalistą o Twoim projekcie.
Zadzwoń teraz