15 questions found
How would you implement caching for a GraphQL API, given that unlike REST, a single endpoint serves widely varying query shapes that don't map cleanly to standard HTTP caching?
Advanced
Because GraphQL typically uses a single POST endpoint (making standard HTTP/CDN caching based on URL largely ineffective, since every distinct query looks identical at the HTTP level) and different clients request different field combinations, caching strategies instead operate at other levels: persisted queries (registering common queries server-side ahead of time and referencing them by a short hash, which also enables safe GET-based caching), response caching keyed by the specific query and variables combination, and field-level or entity-level caching (like Apollo Server's cache hints, specifying how long a specific type or field's data should be cached) integrated with a backing store like Redis.
const resolvers = {
Query: {
product: (_, { id }, { cache }) => {
// Apollo cache hints: this field's result can be cached for 60 seconds
return cache.wrap(`product:${id}`, () => db.products.findById(id), { ttl: 60 });
},
},
};
Real-world example
A high-traffic GraphQL API implements Automatic Persisted Queries, letting the CDN cache and serve the vast majority of repeated identical queries via GET requests keyed by a short query hash, dramatically reducing load on the origin GraphQL server for its most common queries.
Common follow-ups: How do Automatic Persisted Queries specifically enable GET-based caching that a typical GraphQL POST request can't achieve?;What's the tradeoff of field-level cache hints versus caching an entire query's response as a single unit?
Caching;HTTP & Web Servers
What is the purpose of GraphQL's non-null (!) type modifier, and what happens if a resolver for a non-null field returns null unexpectedly?
Intermediate
Appending ! to a type (like String! or [Post!]!) declares that field as non-nullable, meaning the schema guarantees clients will always receive a value, never null -- if a resolver for a non-null field somehow returns null or throws an error, GraphQL's null-propagation behavior bubbles that null upward to the nearest nullable ancestor field in the query, potentially nulling out an entire larger portion of the response (or the whole response, if there's no nullable ancestor at all) rather than allowing the type contract to be silently violated.
type Query { user(id: ID!): User! } # this field promises to always return a User, never null
# If the resolver for 'user' throws or returns null unexpectedly,
# GraphQL nulls out the nearest nullable ancestor -- potentially the entire
# 'data' field of the response if there's no nullable field above it
Real-world example
A team debugging why an entire GraphQL response unexpectedly came back as null (with no data at all) traces it to a single deeply nested non-null field's resolver throwing an error, whose null then propagated all the way up through several non-null ancestor fields until it reached the top-level response itself.
Common follow-ups: Why does GraphQL's null-propagation behavior sometimes surprise developers coming from REST, where an error in one part of the response wouldn't invalidate the entire payload?;How would you design a schema to minimize the blast radius of a single field's failure, using nullable types more strategically?
Error Handling;RESTful API Design with Express
How would you write automated tests for GraphQL resolvers in Node.js, testing them independently of the full HTTP/schema execution layer?
Advanced
Resolvers are typically plain functions (or objects containing functions), letting them be unit tested directly by calling them with mock arguments and a mock context, without needing to spin up a full Apollo Server instance or send actual GraphQL query strings over HTTP -- for more complete integration-level testing that verifies the schema itself is correctly wired up, a library like apollo-server-testing (or executing queries against a test server instance in-memory) can run full GraphQL queries against the actual schema and resolvers together.
test('user resolver returns the requested user', async () => {
const mockContext = { dataSources: { userAPI: { getUser: jest.fn().mockResolvedValue({ id: '1', name: 'Alice' }) } } };
const result = await resolvers.Query.user(null, { id: '1' }, mockContext);
expect(result.name).toBe('Alice');
});
Real-world example
A team unit tests each resolver function in isolation with mocked data sources for fast, focused tests, while maintaining a smaller suite of integration tests that execute complete GraphQL queries against a real in-memory test server instance to verify the schema and resolvers are correctly wired together end-to-end.
Common follow-ups: What's the tradeoff between fast, isolated resolver unit tests and slower, more complete integration tests executing real GraphQL queries?;How would you mock a DataLoader instance specifically for testing a resolver that depends on it?
Testing with Jest
Mocha & the Node Test Runner;Databases & ORMs (MongoDB/Mongoose
SQL/Sequelize)
What is the difference between a GraphQL scalar type and an object type, and how would you define a custom scalar (like DateTime) in a Node.js GraphQL server?
Intermediate
Scalar types represent primitive, leaf-level values (the built-in ones are Int, Float, String, Boolean, and ID) that can't have their own sub-fields queried, while object types represent structured data with their own set of fields that can themselves be queried further -- GraphQL also supports custom scalars for domain-specific primitive values not covered by the built-ins (like a properly-validated DateTime or Email), implemented by providing serialize, parseValue, and parseLiteral functions that define how that custom type converts between its internal representation and the wire format.
const { GraphQLScalarType } = require('graphql');
const DateTimeScalar = new GraphQLScalarType({
name: 'DateTime',
serialize: (value) => value.toISOString(),
parseValue: (value) => new Date(value),
});
const resolvers = { DateTime: DateTimeScalar, /* ... */ };
Real-world example
An events API defines a custom DateTime scalar to properly serialize and parse date values consistently as ISO 8601 strings across the entire schema, rather than representing timestamps as plain, ambiguous strings or numbers that each client might interpret differently.
Common follow-ups: What validation or parsing errors should a custom scalar's parseValue function raise if given genuinely invalid input?;Are there well-maintained community packages providing common custom scalars like DateTime and JSON, rather than implementing them from scratch?
RESTful API Design with Express;Date
Time & Internationalization
What is the difference between a GraphQL query variable and hardcoding a value directly inside the query string?
Beginner
Query variables let you parameterize a GraphQL query with dynamic values passed separately from the query document itself (similar in spirit to a parameterized SQL query), rather than string-interpolating values directly into the query text -- this makes queries reusable across different inputs without needing to construct a new query string each time, and importantly avoids a class of injection-like bugs that could arise from improperly escaping interpolated values directly into the query syntax.
# Using a variable, defined separately from the query string
query GetUser($userId: ID!) {
user(id: $userId) { name, email }
}
// Sent alongside the query as a separate variables object
{ "userId": "123" }
Real-world example
A frontend application defines a single parameterized GetUser query once and reuses it for every different user ID needed throughout the app, simply passing a different variables object each time rather than constructing a brand new query string with the ID interpolated directly for every single request.
Common follow-ups: Why is directly interpolating a value into a GraphQL query string considered a worse practice than using variables, beyond just convenience?;How do query variables interact with GraphQL client libraries' caching mechanisms, like Apollo Client's normalized cache?
Security;RESTful API Design with Express