From atum-stack-backend
Apollo GraphQL pattern library — Apollo Server (the standard Node.js GraphQL server with pluggable data sources, batch loading via DataLoader, file uploads, subscriptions over WebSocket / SSE), Apollo Client (the leading TypeScript GraphQL client with normalized cache, optimistic updates, fragment colocation, query / mutation / subscription hooks for React / Vue / Angular / Svelte), Apollo Federation v2 (subgraphs + supergraph composition for microservices, entity resolution with @key directive, value types with @shareable, contextual constraints with @inaccessible / @tag), Apollo Router (Rust-based federation router replacing Apollo Gateway, query planning + caching + observability), Apollo Connectors for adding REST APIs to GraphQL without resolvers, schema design best practices (avoid n+1 with DataLoader, pagination via Relay cursor connections, errors as data via union types, nullable by default), security (depth limit, query complexity, persisted queries, CSRF prevention, rate limiting), schema first vs code first, codegen (graphql-codegen for TypeScript types), Apollo Studio for schema registry + metrics, Apollo MCP server for AI agents to introspect and query GraphQL APIs, and migration from REST to GraphQL. Use when building any GraphQL API or client with Apollo, federating multiple services, or migrating REST endpoints to GraphQL. Differentiates from generic api-design skill by Apollo-specific tooling and federation patterns.
npx claudepluginhub arnwaldn/atum-plugins-collection --plugin atum-stack-backendThis skill uses the workspace's default tool permissions.
Patterns canoniques pour utiliser **Apollo GraphQL** côté serveur et client en 2026.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Guides implementation of event-driven hooks in Claude Code plugins using prompt-based validation and bash commands for PreToolUse, Stop, and session events.
Patterns canoniques pour utiliser Apollo GraphQL côté serveur et client en 2026.
npm install @apollo/server graphql @apollo/datasource-rest dataloader
// server.ts
import { ApolloServer } from '@apollo/server'
import { startStandaloneServer } from '@apollo/server/standalone'
import { typeDefs } from './schema.js'
import { resolvers } from './resolvers.js'
const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
introspection: process.env.NODE_ENV !== 'production',
formatError: (formattedError, error) => {
// Strip stack traces in production
if (process.env.NODE_ENV === 'production') {
return { message: formattedError.message, code: formattedError.extensions?.code }
}
return formattedError
},
})
const { url } = await startStandaloneServer(server, {
context: async ({ req }) => ({
user: await authenticateUser(req.headers.authorization),
loaders: createDataLoaders(),
}),
listen: { port: 4000 },
})
console.log(`🚀 Server ready at ${url}`)
# schema.graphql
type Query {
user(id: ID!): User
users(first: Int = 10, after: String): UserConnection!
search(query: String!, type: SearchType): SearchResult!
}
type Mutation {
createUser(input: CreateUserInput!): CreateUserPayload!
updateUser(input: UpdateUserInput!): UpdateUserPayload!
}
type Subscription {
userOnline(roomId: ID!): User!
}
# Relay-style cursor pagination
type UserConnection {
edges: [UserEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type UserEdge {
node: User!
cursor: String!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
# Errors as data (union)
union CreateUserPayload = CreateUserSuccess | UserError
type CreateUserSuccess {
user: User!
}
type UserError {
message: String!
field: String
code: ErrorCode!
}
enum ErrorCode {
INVALID_INPUT
DUPLICATE
NOT_FOUND
UNAUTHORIZED
}
import DataLoader from 'dataloader'
export function createDataLoaders() {
return {
userById: new DataLoader<string, User>(async (ids) => {
const users = await db.user.findMany({ where: { id: { in: [...ids] } } })
const map = new Map(users.map(u => [u.id, u]))
return ids.map(id => map.get(id) || new Error(`User ${id} not found`))
}),
}
}
// In resolver
const resolvers = {
Post: {
author: (post, _, { loaders }) => loaders.userById.load(post.authorId),
},
}
Sans DataLoader, requêter 100 posts → 100 SELECT auteur. Avec DataLoader → 1 SELECT batched.
import { ApolloClient, InMemoryCache, ApolloProvider, gql } from '@apollo/client'
const client = new ApolloClient({
uri: 'https://api.example.com/graphql',
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
users: {
// Pagination merge
keyArgs: false,
merge(existing, incoming) {
return {
...incoming,
edges: [...(existing?.edges ?? []), ...incoming.edges],
}
},
},
},
},
},
}),
headers: {
authorization: `Bearer ${token}`,
},
})
function App() {
return (
<ApolloProvider client={client}>
<UsersList />
</ApolloProvider>
)
}
const GET_USERS = gql`
query GetUsers($first: Int!, $after: String) {
users(first: $first, after: $after) {
edges {
node { id name email }
}
pageInfo { hasNextPage endCursor }
}
}
`
function UsersList() {
const { data, loading, fetchMore } = useQuery(GET_USERS, { variables: { first: 20 } })
// ...
}
# Subgraph: users
extend schema @link(url: "https://specs.apollo.dev/federation/v2.5", import: ["@key", "@shareable"])
type User @key(fields: "id") {
id: ID!
name: String!
email: String!
}
type Query {
user(id: ID!): User
}
# Subgraph: posts
extend schema @link(url: "https://specs.apollo.dev/federation/v2.5", import: ["@key"])
type Post @key(fields: "id") {
id: ID!
title: String!
content: String!
author: User!
}
type User @key(fields: "id") {
id: ID! @external
posts: [Post!]!
}
# Compose supergraph
rover supergraph compose --config supergraph.yaml > supergraph.graphql
# Run Apollo Router (Rust)
./router --supergraph supergraph.graphql
import { ApolloServerPluginLandingPageDisabled } from '@apollo/server/plugin/disabled'
import depthLimit from 'graphql-depth-limit'
import { createComplexityRule } from 'graphql-validation-complexity'
const server = new ApolloServer({
typeDefs,
resolvers,
validationRules: [
depthLimit(7), // max query depth
createComplexityRule({ maximumComplexity: 1000 }),
],
plugins: [
process.env.NODE_ENV === 'production'
? ApolloServerPluginLandingPageDisabled()
: ApolloServerPluginLandingPageGraphQLPlayground(),
],
csrfPrevention: true, // require Apollo-Require-Preflight header
})
Persisted queries : ne permettre que les queries pré-approuvées en prod (anti-DDoS).
# Generate persisted query manifest
npx generate-persisted-query-manifest
# codegen.yml
schema: https://api.example.com/graphql
documents: 'src/**/*.{ts,tsx}'
generates:
src/generated/graphql.ts:
plugins:
- typescript
- typescript-operations
- typed-document-node
npm install -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations @graphql-codegen/typed-document-node
graphql-codegen
Résultat : types TypeScript auto-générés depuis le schema → end-to-end type safety.
@apollo/client v3 sans normalized cache config — re-fetches inutilesapi-designer (ce plugin)grpc-patterns (à créer si besoin)api-design (ce plugin)atum-stack-web)