Occurs when a resolver makes one query to fetch a list, then N additional queries for related data on each item. For 10 posts fetching authors separately, it's 1 + 10 = 11 queries instead of 2. Example: posts.forEach(post => getAuthor(post.authorId)) creates N queries. Solved using DataLoader which batches requests within the same execution cycle. DataLoader 3.0 uses breadth-first loading, reducing concurrency from O(N²) to O(1) for 5x performance improvement. Critical for nested resolvers in GraphQL.
GraphQL FAQ & Answers
25 expert GraphQL answers researched from official documentation. Every answer cites authoritative sources you can verify.
unknown
25 questionsA utility that batches multiple .load(key) calls into a single batchLoadFn(keys) call and caches results per request. Create new instance per request: new DataLoader(async (keys) => batchGetUsers(keys)). Return results in same order as input keys. Converts N separate database queries into one batched query. Best practice: scope DataLoader instances per-request in GraphQL context to prevent data leakage. DataLoader 3.0 introduces breadth-first loading for more efficient resolution. Use focused batch functions—one loader per data access pattern.
A typed definition of API structure describing available queries, mutations, subscriptions, and custom types. Written in GraphQL Schema Definition Language (SDL): type Query { user(id: ID!): User }. Serves as contract between client and server. Example: type User { id: ID! name: String! email: String! }. September 2025 spec added Schema Coordinates for precise element referencing, OneOf Input Objects for mutually exclusive inputs, Operation Descriptions, and full Unicode support. Apollo Server 4 uses schema-first approach. Enforces type safety at runtime with automatic validation of queries against schema.
Queries read data (side-effect free operations like GET): query { user(id: "1") { name } }. Mutations modify data (create, update, delete like POST/PUT/DELETE): mutation { createUser(name: "Alice") { id } }. Critical difference: queries can run in parallel for performance; mutations run sequentially by GraphQL spec to avoid race conditions. Always use mutation for state changes even if technically query could work. Return modified object from mutations to update Apollo Client cache automatically.
Real-time push-based data delivery using WebSockets or Server-Sent Events (SSE). Clients subscribe to events: subscription { messageAdded { id text } } and receive updates when data changes. Apollo Server 4 requires graphql-ws library implementing the GraphQL over WebSocket Protocol. Legacy protocols subscriptions-transport-ws and graphql-transport-ws are deprecated—use graphql-ws for new applications. Setup requires http.Server wrapping Express + WebSocket server—not compatible with startStandaloneServer(). graphql-ws supports multiplexing multiple subscriptions over single connection with simplified stop mechanism. Use for live notifications, chat, collaborative editing, real-time dashboards.
A function that resolves a value for a schema field. Each field in a query has a resolver that fetches data: User: { email: (parent) => parent.email_address }. Signature: (parent, args, context, info) => result. Parent contains data from parent resolver, args are field arguments, context is shared per-request (auth, DataLoader), info contains query metadata. Resolvers execute independently and compose. Default resolver returns parent[fieldName]. Use context for DataLoader instances and authentication. Chain resolvers for nested data fetching.
An architecture for composing multiple GraphQL services into a single unified graph. Each subgraph owns its domain: extend type User @key(fields: "id") { orders: [Order] }. Apollo Gateway or Apollo Router combines schemas, enabling distributed teams to work independently. Federation 2 (current standard, v2.11+) improves type merging and shared ownership with first-class support for shared interfaces and enums. Gateway handles query planning across subgraphs. GraphQL Foundation's Composite Schema Working Group (includes Apollo, ChilliCream, Hasura, Netflix, The Guild) presented standardization efforts at GraphQLConf 2025 to create vendor-neutral federation specifications. Use for microservices, domain-driven design, multi-team scaling.
Querying a GraphQL API to discover its schema, types, fields, and documentation at runtime: { __schema { types { name fields { name } } } }. Powers tools like GraphQL Playground, GraphiQL, Apollo Studio, and enables automatic client code generation. Every field supports __type meta-field. Disable in production for security: introspection: false in Apollo Server 4 config. Alternative: use schema registry for client discovery instead of introspection. Enable for development/staging environments only. Attackers use introspection to map attack surface.
A comprehensive state management library for JavaScript that integrates with React, Vue, or Angular. Handles data fetching, normalized caching, local state, error handling, and optimistic UI for GraphQL. Version 3+ uses InMemoryCache with reference-based normalization: objects with id + __typename cached as flat lookup table. Setup: new ApolloClient({ cache: new InMemoryCache(), link: new HttpLink({ uri: '/graphql' }) }). Supports automatic cache updates, refetchQueries, cache.writeFragment for manual updates. Use @apollo/client package (consolidates multiple v2 packages).
A JavaScript framework from Meta for building data-driven React applications with GraphQL. Enforces conventions: Node interface (id field), Connection pattern for pagination (edges, pageInfo), fragment colocation. Relay Compiler generates optimized code at build time with static validation. Features fragment composition, automatic query deduplication, garbage collection of unused cache entries. Modern Relay uses Hooks API: useFragment, useLazyLoadQuery, usePaginationFragment. More opinionated than Apollo but extremely performant for large apps. Use for Facebook-scale applications with complex data requirements.
By default fields are nullable (can return null): name: String. Non-null types use ! suffix: name: String! and guarantee a value will always be returned or entire query fails with error. Use null for optional fields, non-null for required ones. Nullable lists: [String] (list or null), [String]! (list always, items nullable), [String!]! (list always, items non-null). Design tip: prefer nullable by default for resilience—one field error doesn't crash entire query. Non-null should represent true business invariants only.
A reusable unit of fields that can be included in multiple queries or mutations: fragment UserFields on User { id name email }, then use: query { user { ...UserFields } }. Enables component colocation where UI components define their data requirements. Inline fragments for unions/interfaces: ... on Admin { permissions }. Relay enforces fragment colocation pattern. Apollo Client supports fragment matching for cache updates. Use @defer directive with fragments for incremental delivery: ...UserFields @defer. Fragments reduce duplication and promote maintainability in large GraphQL apps.
Clients request exactly the fields they need in a single query: query { user(id: "1") { name email } } instead of getting all user fields. Eliminates over-fetching (getting unwanted data like passwords, metadata) and under-fetching (multiple round trips for related data). One query can fetch user + posts + comments in single request with nested resolvers. Reduces bandwidth by ~40-60% compared to REST in typical apps. Mobile apps benefit most—less data transfer, fewer HTTP requests. Use field-level permissions to hide sensitive fields. GraphQL N+1 problem requires DataLoader to maintain performance benefits.
Uses opaque cursor strings to mark positions in datasets, following Relay Connection specification: { users(first: 10, after: "cursor123") { edges { cursor node { id name } } pageInfo { hasNextPage endCursor } } }. More reliable than offset pagination for real-time data—handles insertions/deletions correctly without skipping/duplicating items. Cursor is base64-encoded position. Use WHERE with cursors to leverage database indexes, whereas OFFSET must scan over records. Ideal for infinite scroll, social feeds, live data. Offset pagination only for static data with numbered pages. Performance: cursor scales better for large datasets.
A type representing one of multiple possible object types: union SearchResult = User | Post | Comment. Search results could return heterogeneous types. Clients use inline fragments with __typename to handle different result types: { search { __typename ... on User { name } ... on Post { title } } }. Union types cannot include interfaces or other unions. Use when field can return different unrelated types. Alternative: interface when types share common fields. Apollo Client uses __typename for cache normalization of union results. Common for search, activity feeds, notifications.
An abstract type defining common fields that multiple object types must implement: interface Node { id: ID! }, then type User implements Node { id: ID! name: String! }. Objects implementing interfaces can add additional fields beyond interface requirements. Use inline fragments to query interface-specific fields: { node(id: "1") { id ... on User { name } } }. Relay requires Node interface with id field for object identification. Apollo Client supports __typename-based polymorphic cache normalization. Use for shared behavior across types (e.g., Timestamped, Likeable interfaces).
A spec-compliant, production-ready GraphQL server for Node.js. Apollo Server 5 is current (2025); Apollo Server 4 reaches EOL January 26, 2026. Uses @apollo/server package with schema-first approach and TypeScript support. Setup: new ApolloServer({ typeDefs, resolvers }). Features: automatic schema validation, GraphQL Playground (dev), introspection control, subscriptions (via graphql-ws), file uploads, plugin system for logging/metrics. Use expressMiddleware from @as-integrations/express4 (or express5) for subscriptions + custom middleware. Integrates with Express, Fastify, Lambda. Production features: persisted queries, query depth limiting, cost analysis. Migrate to Apollo Server 5 before January 2026 EOL.
Arguments are field-level parameters defined in schema: user(id: "123") { name } hardcoded in query. Variables are query-level parameters passed separately for reusability, validation, and security: query GetUser($userId: ID!) { user(id: $userId) { name } } with { "userId": "123" } in variables object. Variables prevent injection attacks—values are properly typed and validated. Enable query reuse—same query string with different variables can be cached. Required for Automatic Persisted Queries (APQ). Use variables for all dynamic values, never string interpolation.
Combining multiple GraphQL operations into a single HTTP request to reduce network overhead. Apollo Link Batch HTTP automatically batches queries sent within configurable time window (default 10ms): new BatchHttpLink({ uri: '/graphql', batchMax: 5, batchInterval: 20 }). Server must support batched requests (Apollo Server 4 does). Reduces HTTP overhead for mobile apps. Trade-off: adds latency (waits for batch window). Not same as DataLoader (batching is network-level, DataLoader is resolver-level). Combine both for optimal performance. Use for high-latency networks, mobile apps.
Annotations that modify execution behavior, prefixed with @: query GetUser($withEmail: Boolean!) { user { name email @include(if: $withEmail) } }. Built-in directives: @include (conditional inclusion), @skip (conditional exclusion), @deprecated (mark fields obsolete with reason). Custom directives for authorization: field @auth(requires: ADMIN), rate limiting, caching. Apollo Server 4 uses SchemaDirectiveVisitor for custom directives. Directive locations: FIELD, FRAGMENT_SPREAD, INLINE_FRAGMENT, QUERY, MUTATION. Use @defer/@stream for incremental delivery (experimental). Schema directives vs query directives.
A client-side cache storing query results as flat lookup table of objects by unique identifiers (id + __typename): { 'User:1': { id: '1', name: 'Alice' } }. Apollo Client 3+ uses reference-based approach—only entities with ID are normalized; entities without ID stored within parent. Enables efficient updates without refetching. Example: mutation updating User:1 automatically updates all queries containing that user. Configure with cache policies: new InMemoryCache({ typePolicies: { User: { keyFields: ['email'] } } }). Use cache.writeFragment for optimistic UI. Garbage collection removes unreferenced objects. Cache size reduced ~50% vs Apollo Client 2.
A meta field available on every GraphQL object returning the type name as a string: { user { __typename id name } } returns { __typename: "User", id: "1", name: "Alice" }. Essential for Apollo Client cache normalization (generates cache key: User:1). Required for union/interface type discrimination in client code. Automatically added by Apollo Client to all queries. Use in conditional rendering: if (result.__typename === 'User'). Relay Compiler requires __typename for fragment matching. No performance cost—computed during execution, not fetched from database. Always include for polymorphic types.
Primitive leaf types that resolve to concrete values. Built-in scalars: Int (32-bit signed), Float (double precision), String (UTF-8), Boolean, ID (unique identifier, serialized as string). Custom scalars for specialized validation: scalar DateTime, scalar Email, scalar URL. Implement serialize (outgoing), parseValue (incoming variable), parseLiteral (inline value) methods. Apollo Server 4: import { DateTimeResolver } from 'graphql-scalars'. Use graphql-scalars library for common types (DateTime, JSON, EmailAddress). Scalars enforce validation at type level. Always validate in resolvers too for security.
APQ improves network performance by sending SHA-256 query hashes (64 bytes) instead of full query strings after first request. Apollo Server 4 supports APQ without configuration. Client setup: createPersistedQueryLink({ sha256 }).concat(new HttpLink({ uri: '/graphql' })). Server caches queries by hash with configurable TTL: persistedQueries: { ttl: 900 }. When client sends hash, server retrieves cached query; if not found, client resends full query. Reduces bandwidth dramatically, enables GET requests for CDN caching with useGETForHashedQueries: true. Note: APQ does not provide security features; use registered Persisted Operations for operation safelisting. Ideal for mobile networks and high-traffic deployments with external cache (Redis/Memcached).
Special object types for passing complex objects as mutation arguments: input CreateUserInput { name: String! email: String! }, used in mutation: createUser(input: CreateUserInput!): User. Defined with 'input' keyword instead of 'type'. Cannot have arguments on fields, cannot reference output types (only scalars, enums, other input types), ensuring clear separation of concerns. September 2025 spec added @oneOf directive for mutually exclusive inputs: input UserIdentifier @oneOf { id: ID email: String username: String } ensures exactly one field is provided. Use for mutations with multiple parameters to avoid long argument lists. Supports nested input types for complex data structures. Validate input in resolvers before processing.