SKILL.md
readonlyread-only
name
graphql-apollo-server
description
Build production GraphQL servers with Apollo Server, plugins, and federation
version
"2.0.0"
Apollo Server Skill
Deploy production-ready GraphQL APIs
Overview
Learn to build scalable GraphQL servers with Apollo Server 4, including middleware integration, custom plugins, federation, and production best practices.
Quick Reference
| Feature | Package | Purpose |
|---|---|---|
| Server | @apollo/server |
Core server |
| Express | @apollo/server/express4 |
Express integration |
| Plugins | @apollo/server/plugin/* |
Extensibility |
| Federation | @apollo/subgraph |
Microservices |
Core Setup
1. Basic Server (Express)
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');
});
}
2. Production Configuration
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,
],
});
3. Custom Plugins
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');
}
},
};
},
};
4. Federation (Subgraph)
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 }),
});
5. Response Caching
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)
}
`;
6. Health Checks
// 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 });
});
Troubleshooting
| 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 |
Debug Commands
# 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 } } }"}'
Usage
Skill("graphql-apollo-server")
Related Skills
graphql-resolvers- Resolver implementationgraphql-security- Security configurationgraphql-codegen- Type generation
Related Agent
04-graphql-apollo-server- For detailed guidance
You Might Also Like
Related Skills

gog
169Kdev-api
Google Workspace CLI for Gmail, Calendar, Drive, Contacts, Sheets, and Docs.
openclaw
orpc-contract-first
127Kdev-api
Guide for implementing oRPC contract-first API patterns in Dify frontend. Triggers when creating new API contracts, adding service endpoints, integrating TanStack Query with typed contracts, or migrating legacy service calls to oRPC. Use for all API layer work in web/contract and web/service directories.
langgenius

