Subchapter 9.6
references/troubleshooting.mdMarkdown10 KBView on GitHub
Error: Cannot find module '@apollo/server'
# Ensure correct packages are installed
npm install @apollo/server graphql
# For Express integration
npm install @apollo/server express graphql cors
# Clear node_modules and reinstall if issues persist
rm -rf node_modules package-lock.json
npm installError: Cannot find module '@apollo/server/standalone'
This is a subpath export. Ensure:
moduleResolution is bundler, node16, or nodenextError: Cannot use import statement outside a module
// package.json
{
"type": "module"
}
// tsconfig.json
{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"esModuleInterop": true,
"target": "ES2022"
}
}Error: Property 'xxx' does not exist on type 'BaseContext'
// Define and use typed context
interface MyContext {
user?: User;
dataSources: DataSources;
}
const server = new ApolloServer<MyContext>({ typeDefs, resolvers });Apollo Server 4 is ESM-first. For CommonJS projects:
// Use dynamic import
const { ApolloServer } = await import('@apollo/server');
// Or configure tsconfig for interop
{
"compilerOptions": {
"module": "CommonJS",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
}
}Error: Unknown type "User". Did you mean...
// Ensure all types are defined in typeDefs
const typeDefs = `#graphql
type Query {
user(id: ID!): User # User must be defined
}
type User { # Define the type
id: ID!
name: String!
}
`;Error: Cannot return null for non-nullable field Query.user
// Schema declares non-null
type Query {
user(id: ID!): User! # ! means non-null
}
// Resolver must return a value
const resolvers = {
Query: {
user: async (_, { id }, { dataSources }) => {
const user = await dataSources.usersAPI.getById(id);
if (!user) {
throw new GraphQLError('User not found'); // Throw, don't return null
}
return user;
},
},
};Error: Enum "Status" cannot represent value: "draft"
// Schema defines uppercase
enum Status {
DRAFT
PUBLISHED
}
// But database returns lowercase
// Solution: Map enum values
const resolvers = {
Status: {
DRAFT: 'draft',
PUBLISHED: 'published',
},
// Or transform in resolver
Post: {
status: (parent) => parent.status.toUpperCase(),
},
};Error: Variable "$input" got invalid value
# Schema
input CreateUserInput {
email: String!
name: String!
}
# Query - ensure variable matches input type exactly
mutation CreateUser($input: CreateUserInput!) {
createUser(input: $input) { id }
}
# Variables - must match schema structure
{
"input": {
"email": "test@example.com",
"name": "Test User"
}
}Error: Cannot read properties of undefined (reading 'user')
// Ensure context function returns complete object
const context = async ({ req }) => {
// Always return all expected properties
return {
user: (await getUser(req.headers.authorization)) ?? null,
dataSources: {
usersAPI: new UsersAPI(),
},
};
};Error: [object Promise] returned instead of actual data
// Bad - missing await
const resolvers = {
Query: {
user: (_, { id }, { dataSources }) => {
dataSources.usersAPI.getById(id); // Missing return/await
},
},
};
// Good - return promise or use async/await
const resolvers = {
Query: {
user: (_, { id }, { dataSources }) => {
return dataSources.usersAPI.getById(id); // Return promise
},
// OR
user: async (_, { id }, { dataSources }) => {
return await dataSources.usersAPI.getById(id); // Async/await
},
},
};Error: Converting circular structure to JSON
// Avoid returning raw ORM objects with circular refs
const resolvers = {
Query: {
user: async (_, { id }) => {
const user = await prisma.user.findUnique({
where: { id },
include: { posts: { include: { author: true } } }, // Circular!
});
// Transform to plain object
return {
id: user.id,
name: user.name,
posts: user.posts.map((p) => ({ id: p.id, title: p.title })),
};
},
},
};Symptom: Slow queries, many database calls
// Problem: Each user triggers separate posts query
const resolvers = {
User: {
posts: (parent) => db.posts.findByAuthor(parent.id), // N queries
},
};
// Solution: Use DataLoader
import DataLoader from "dataloader";
const context = async () => ({
loaders: {
postsByAuthor: new DataLoader(async (authorIds) => {
const posts = await db.posts.findByAuthorIds(authorIds); // 1 query
return authorIds.map((id) => posts.filter((p) => p.authorId === id));
}),
},
});
const resolvers = {
User: {
posts: (parent, _, { loaders }) => loaders.postsByAuthor.load(parent.id),
},
};Symptom: Memory usage grows over time
// Problem: Shared data source instances
const sharedAPI = new UsersAPI(); // Wrong!
const context = async () => ({ dataSources: { usersAPI: sharedAPI } });
// Solution: Create per-request instances
const context = async () => ({
dataSources: {
usersAPI: new UsersAPI(), // New instance per request
},
});
// Problem: DataLoader created once
const loader = new DataLoader(batchFn); // Wrong - caches forever!
// Solution: Create per-request DataLoaders
const context = async () => ({
loaders: {
userLoader: new DataLoader(batchFn), // New instance per request
},
});Symptom: Timeout or memory errors on large queries
// Add pagination
type Query {
users(limit: Int = 10, offset: Int = 0): [User!]!
}
// Limit query depth
import depthLimit from 'graphql-depth-limit';
const server = new ApolloServer({
typeDefs,
resolvers,
validationRules: [depthLimit(5)],
});
// Add query complexity limit
import { createComplexityLimitRule } from 'graphql-validation-complexity';
const server = new ApolloServer({
typeDefs,
resolvers,
validationRules: [createComplexityLimitRule(1000)],
});Error: Access-Control-Allow-Origin header missing
import cors from "cors";
// Express integration - add cors before middleware
app.use(
"/graphql",
cors({
origin: ["http://localhost:3000", "https://myapp.com"],
credentials: true,
}),
express.json(),
expressMiddleware(server),
);
// Standalone - configure cors option
const { url } = await startStandaloneServer(server, {
listen: { port: 4000 },
context: async ({ req }) => ({
/* ... */
}),
// Standalone has basic CORS enabled by default
});Error: req.body is undefined or empty
// Express - ensure json middleware is before Apollo
app.use(express.json()); // Must come before expressMiddleware
app.use(
"/graphql",
cors(),
express.json(), // JSON parser is required
expressMiddleware(server),
);Error: Subscriptions not working
// Apollo Server 4 doesn't include subscription support by default
// Use graphql-ws package
import { WebSocketServer } from "ws";
import { useServer } from "graphql-ws/lib/use/ws";
import { makeExecutableSchema } from "@graphql-tools/schema";
const schema = makeExecutableSchema({ typeDefs, resolvers });
const wsServer = new WebSocketServer({
server: httpServer,
path: "/graphql",
});
useServer({ schema }, wsServer);const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [
{
async requestDidStart({ request }) {
console.log("Query:", request.query);
console.log("Variables:", JSON.stringify(request.variables, null, 2));
return {
async willSendResponse({ response }) {
console.log("Response:", JSON.stringify(response.body, null, 2));
},
};
},
},
],
});const resolvers = {
Query: {
debug: (_, __, context) => {
console.log("Context keys:", Object.keys(context));
console.log("User:", context.user);
return "Check server logs";
},
},
};// Unit test resolvers without server
import { resolvers } from "./resolvers";
describe("Query.user", () => {
it("returns user by id", async () => {
const mockDataSources = {
usersAPI: {
getById: jest.fn().mockResolvedValue({ id: "1", name: "Test" }),
},
};
const result = await resolvers.Query.user(undefined, { id: "1" }, { dataSources: mockDataSources });
expect(result).toEqual({ id: "1", name: "Test" });
});
});import { printSchema } from "graphql";
import { makeExecutableSchema } from "@graphql-tools/schema";
const schema = makeExecutableSchema({ typeDefs, resolvers });
console.log(printSchema(schema));Enable Apollo Sandbox for interactive debugging:
import { ApolloServerPluginLandingPageLocalDefault } from "@apollo/server/plugin/landingPage/default";
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [ApolloServerPluginLandingPageLocalDefault({ embed: true })],
});Open http://localhost:4000/graphql in browser to access Sandbox.