Build production GraphQL servers with Apollo Server, plugins, and federation
Build production GraphQL servers with Apollo Server 4, Express integration, and custom plugins. Use this when creating GraphQL APIs that need middleware, federation, response caching, or graceful shutdown handling.
/plugin marketplace add pluginagentmarketplace/custom-plugin-graphql/plugin install developer-roadmap@pluginagentmarketplace-graphqlThis skill inherits all available tools. When active, it can use any tool Claude has access to.
assets/config.yamlassets/schema.jsonreferences/GUIDE.mdreferences/PATTERNS.mdscripts/validate.pyDeploy production-ready GraphQL APIs
Learn to build scalable GraphQL servers with Apollo Server 4, including middleware integration, custom plugins, federation, and production best practices.
| Feature | Package | Purpose |
|---|---|---|
| Server | @apollo/server | Core server |
| Express | @apollo/server/express4 | Express integration |
| Plugins | @apollo/server/plugin/* | Extensibility |
| Federation | @apollo/subgraph | Microservices |
import { ApolloServer } from '@apollo/server';
import { expressMiddleware } from '@apollo/server/express4';
import { ApolloServerPluginDrainHttpServer } from '@apollo/server/plugin/drainHttpServer';
import express from 'express';
import http from 'http';
import cors from 'cors';
interface Context {
user: User | null;
dataSources: DataSources;
}
async function startServer() {
const app = express();
const httpServer = http.createServer(app);
const server = new ApolloServer<Context>({
typeDefs,
resolvers,
plugins: [
// Graceful shutdown
ApolloServerPluginDrainHttpServer({ httpServer }),
],
});
await server.start();
app.use(
'/graphql',
cors({ origin: ['http://localhost:3000'], credentials: true }),
express.json(),
expressMiddleware(server, {
context: async ({ req }) => ({
user: await getUser(req),
dataSources: createDataSources(),
}),
}),
);
httpServer.listen(4000, () => {
console.log('Server ready at http://localhost:4000/graphql');
});
}
const server = new ApolloServer<Context>({
typeDefs,
resolvers,
// Error formatting
formatError: (error) => {
console.error('GraphQL Error:', error);
// Hide internal errors in production
if (process.env.NODE_ENV === 'production') {
if (error.extensions?.code === 'INTERNAL_SERVER_ERROR') {
return { message: 'Internal error', extensions: { code: 'INTERNAL_ERROR' } };
}
}
return error;
},
// Disable introspection in production
introspection: process.env.NODE_ENV !== 'production',
plugins: [
ApolloServerPluginDrainHttpServer({ httpServer }),
loggingPlugin,
complexityPlugin,
],
});
import { ApolloServerPlugin } from '@apollo/server';
// Logging plugin
const loggingPlugin: ApolloServerPlugin<Context> = {
async requestDidStart({ request, contextValue }) {
const start = Date.now();
console.log('Request:', request.operationName);
return {
async willSendResponse() {
console.log(`Completed in ${Date.now() - start}ms`);
},
async didEncounterErrors({ errors }) {
errors.forEach(e => console.error('Error:', e.message));
},
};
},
};
// Query complexity plugin
import { getComplexity, simpleEstimator } from 'graphql-query-complexity';
const complexityPlugin: ApolloServerPlugin<Context> = {
async requestDidStart() {
return {
async didResolveOperation({ schema, document, request }) {
const complexity = getComplexity({
schema,
query: document,
variables: request.variables,
estimators: [simpleEstimator({ defaultComplexity: 1 })],
});
if (complexity > 1000) {
throw new GraphQLError('Query too complex');
}
},
};
},
};
import { buildSubgraphSchema } from '@apollo/subgraph';
import { gql } from 'graphql-tag';
const typeDefs = gql`
extend schema
@link(url: "https://specs.apollo.dev/federation/v2.0",
import: ["@key", "@shareable", "@external"])
type Query {
user(id: ID!): User
}
type User @key(fields: "id") {
id: ID!
name: String!
email: String!
}
`;
const resolvers = {
Query: {
user: (_, { id }) => users.find(u => u.id === id),
},
User: {
__resolveReference: (user) => users.find(u => u.id === user.id),
},
};
const server = new ApolloServer({
schema: buildSubgraphSchema({ typeDefs, resolvers }),
});
import responseCachePlugin from '@apollo/server-plugin-response-cache';
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [
responseCachePlugin({
// Cache key includes user ID for personalized data
sessionId: ({ contextValue }) => contextValue.user?.id || null,
}),
],
});
// Schema hints
const typeDefs = gql`
type Query {
# Cache for 1 hour
popularPosts: [Post!]! @cacheControl(maxAge: 3600)
# Private, user-specific
me: User @cacheControl(maxAge: 0, scope: PRIVATE)
}
`;
// Health endpoint
app.get('/health', async (req, res) => {
const checks = {
server: 'healthy',
database: await checkDb(),
redis: await checkRedis(),
};
const healthy = Object.values(checks).every(c => c === 'healthy');
res.status(healthy ? 200 : 503).json(checks);
});
// Readiness endpoint
app.get('/ready', (req, res) => {
res.status(serverReady ? 200 : 503).json({ ready: serverReady });
});
| Issue | Cause | Solution |
|---|---|---|
| CORS errors | Missing middleware | Add cors() before expressMiddleware |
| 503 on shutdown | No drain | Add DrainHttpServer plugin |
| Memory leak | Global loaders | Create per-request |
| Slow startup | Large schema | Use schema caching |
# Test server
curl http://localhost:4000/health
# Test GraphQL
curl -X POST http://localhost:4000/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{ __typename }"}'
# Introspection
curl -X POST http://localhost:4000/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{ __schema { types { name } } }"}'
Skill("graphql-apollo-server")
graphql-resolvers - Resolver implementationgraphql-security - Security configurationgraphql-codegen - Type generation04-graphql-apollo-server - For detailed guidanceThis skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.