GraphQL / API

    GraphQL i Apollo

    Apollo Server, Apollo Client, URQL, DataLoader, Subscriptions, schema design i autoryzacja w TypeScript i Next.js.

    GraphQL
    Query language
    DataLoader
    Batch queries
    Subscriptions
    Real-time
    Codegen
    Type safety

    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').

    Czytaj dalej

    Powiązane artykuły

    Kontakt

    Skontaktuj się z nami

    Porozmawiajmy o Twoim projekcie. Bezpłatna wycena w ciągu 24 godzin.

    Wyślij zapytanie

    Bezpłatna wycena w 24h
    Bez zobowiązań
    Indywidualne podejście
    Ekspresowa realizacja

    Telefon

    +48 790 814 814

    Pon-Pt: 9:00 - 18:00

    Email

    adam@fotz.pl

    Odpowiadamy w ciągu 24h

    Adres

    Plac Wolności 16

    61-739 Poznań

    Godziny pracy

    Pon - Pt9:00 - 18:00
    Sob - NdzZamknięte

    Wolisz porozmawiać?

    Zadzwoń teraz i porozmawiaj z naszym specjalistą o Twoim projekcie.

    Zadzwoń teraz

    Szanujemy Twoją prywatność

    Używamy plików cookies, aby zapewnić najlepsze doświadczenia na naszej stronie. Klikając "Akceptuję", zgadzasz się na ich użycie.