Builds GraphQL APIs with Apollo Server 4, schema design, resolvers, and data sources. Use when implementing GraphQL servers, building federated graphs, or integrating GraphQL with Node.js frameworks.
Builds GraphQL APIs with Apollo Server 4, including schema design, resolvers, and data sources. Use when implementing GraphQL servers, integrating with Node.js frameworks like Express, or building federated graphs.
/plugin marketplace add mgd34msu/goodvibes-plugin/plugin install goodvibes@goodvibes-marketThis skill inherits all available tools. When active, it can use any tool Claude has access to.
references/dataloader.mdreferences/federation.mdApollo Server is a spec-compliant GraphQL server for Node.js. Version 4+ is framework-agnostic and supports Express, Fastify, Lambda, and more.
npm install @apollo/server graphql
import { ApolloServer } from '@apollo/server'
import { startStandaloneServer } from '@apollo/server/standalone'
// Type definitions
const typeDefs = `#graphql
type User {
id: ID!
email: String!
name: String
posts: [Post!]!
}
type Post {
id: ID!
title: String!
content: String
author: User!
}
type Query {
users: [User!]!
user(id: ID!): User
posts: [Post!]!
}
type Mutation {
createUser(email: String!, name: String): User!
createPost(title: String!, content: String, authorId: ID!): Post!
}
`
// Resolvers
const resolvers = {
Query: {
users: () => db.users.findMany(),
user: (_, { id }) => db.users.findById(id),
posts: () => db.posts.findMany()
},
Mutation: {
createUser: (_, { email, name }) => db.users.create({ email, name }),
createPost: (_, { title, content, authorId }) =>
db.posts.create({ title, content, authorId })
},
User: {
posts: (user) => db.posts.findByAuthor(user.id)
},
Post: {
author: (post) => db.users.findById(post.authorId)
}
}
// Create server
const server = new ApolloServer({
typeDefs,
resolvers
})
// Start server
const { url } = await startStandaloneServer(server, {
listen: { port: 4000 }
})
console.log(`Server ready at ${url}`)
npm install @apollo/server express cors
import { ApolloServer } from '@apollo/server'
import { expressMiddleware } from '@apollo/server/express4'
import express from 'express'
import cors from 'cors'
const app = express()
const server = new ApolloServer({
typeDefs,
resolvers
})
await server.start()
app.use(
'/graphql',
cors(),
express.json(),
expressMiddleware(server, {
context: async ({ req }) => ({
token: req.headers.authorization,
user: await getUserFromToken(req.headers.authorization)
})
})
)
app.listen(4000)
interface Context {
user: User | null
dataSources: {
users: UserDataSource
posts: PostDataSource
}
}
const server = new ApolloServer<Context>({
typeDefs,
resolvers
})
await server.start()
app.use(
'/graphql',
expressMiddleware(server, {
context: async ({ req }): Promise<Context> => {
const token = req.headers.authorization?.replace('Bearer ', '')
const user = token ? await verifyToken(token) : null
return {
user,
dataSources: {
users: new UserDataSource(),
posts: new PostDataSource()
}
}
}
})
)
const resolvers = {
Query: {
me: (_, __, context) => context.user,
users: (_, __, { dataSources }) => dataSources.users.findAll()
},
Mutation: {
createPost: (_, args, { user, dataSources }) => {
if (!user) throw new GraphQLError('Not authenticated')
return dataSources.posts.create({ ...args, authorId: user.id })
}
}
}
input CreateUserInput {
email: String!
name: String
role: Role = USER
}
input UpdateUserInput {
email: String
name: String
role: Role
}
type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User!
}
enum Role {
USER
ADMIN
MODERATOR
}
enum PostStatus {
DRAFT
PUBLISHED
ARCHIVED
}
interface Node {
id: ID!
}
interface Timestamped {
createdAt: DateTime!
updatedAt: DateTime!
}
type User implements Node & Timestamped {
id: ID!
email: String!
createdAt: DateTime!
updatedAt: DateTime!
}
union SearchResult = User | Post | Comment
type Query {
search(query: String!): [SearchResult!]!
}
const resolvers = {
SearchResult: {
__resolveType(obj) {
if (obj.email) return 'User'
if (obj.title) return 'Post'
if (obj.body) return 'Comment'
return null
}
}
}
npm install graphql-scalars
import { DateTimeResolver, EmailAddressResolver } from 'graphql-scalars'
const typeDefs = `#graphql
scalar DateTime
scalar EmailAddress
type User {
email: EmailAddress!
createdAt: DateTime!
}
`
const resolvers = {
DateTime: DateTimeResolver,
EmailAddress: EmailAddressResolver
}
import { GraphQLError } from 'graphql'
const resolvers = {
Query: {
user: async (_, { id }, { dataSources }) => {
const user = await dataSources.users.findById(id)
if (!user) {
throw new GraphQLError('User not found', {
extensions: {
code: 'NOT_FOUND',
argumentName: 'id'
}
})
}
return user
}
}
}
function requireAuth(context: Context) {
if (!context.user) {
throw new GraphQLError('You must be logged in', {
extensions: { code: 'UNAUTHENTICATED' }
})
}
return context.user
}
const resolvers = {
Mutation: {
createPost: (_, args, context) => {
const user = requireAuth(context)
return createPost({ ...args, authorId: user.id })
}
}
}
const server = new ApolloServer({
typeDefs,
resolvers,
formatError: (formattedError, error) => {
// Log error
console.error(error)
// Hide internal errors in production
if (process.env.NODE_ENV === 'production') {
if (formattedError.extensions?.code === 'INTERNAL_SERVER_ERROR') {
return {
message: 'Internal server error',
extensions: { code: 'INTERNAL_SERVER_ERROR' }
}
}
}
return formattedError
}
})
npm install @apollo/datasource-rest
import { RESTDataSource } from '@apollo/datasource-rest'
class UsersAPI extends RESTDataSource {
override baseURL = 'https://api.example.com/'
async getUser(id: string) {
return this.get(`users/${id}`)
}
async getUsers() {
return this.get('users')
}
async createUser(user: CreateUserInput) {
return this.post('users', { body: user })
}
// Caching
override willSendRequest(path, request) {
request.headers['Authorization'] = this.context.token
}
}
import { PrismaClient } from '@prisma/client'
class UserDataSource {
private prisma: PrismaClient
constructor() {
this.prisma = new PrismaClient()
}
async findById(id: string) {
return this.prisma.user.findUnique({ where: { id } })
}
async findAll() {
return this.prisma.user.findMany()
}
async create(data: CreateUserInput) {
return this.prisma.user.create({ data })
}
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
type UserEdge {
node: User!
cursor: String!
}
type UserConnection {
edges: [UserEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type Query {
users(first: Int, after: String): UserConnection!
}
const resolvers = {
Query: {
users: async (_, { first = 10, after }, { dataSources }) => {
const decodedCursor = after ? Buffer.from(after, 'base64').toString() : null
const users = await dataSources.users.findMany({
take: first + 1,
cursor: decodedCursor ? { id: decodedCursor } : undefined,
skip: decodedCursor ? 1 : 0
})
const hasNextPage = users.length > first
const edges = users.slice(0, first).map(user => ({
node: user,
cursor: Buffer.from(user.id).toString('base64')
}))
return {
edges,
pageInfo: {
hasNextPage,
hasPreviousPage: !!after,
startCursor: edges[0]?.cursor,
endCursor: edges[edges.length - 1]?.cursor
},
totalCount: await dataSources.users.count()
}
}
}
}
npm install graphql-ws ws
import { createServer } from 'http'
import { WebSocketServer } from 'ws'
import { useServer } from 'graphql-ws/lib/use/ws'
import { makeExecutableSchema } from '@graphql-tools/schema'
const typeDefs = `#graphql
type Subscription {
messageCreated: Message!
userTyping(channelId: ID!): User!
}
`
const resolvers = {
Subscription: {
messageCreated: {
subscribe: () => pubsub.asyncIterator(['MESSAGE_CREATED'])
},
userTyping: {
subscribe: (_, { channelId }) =>
pubsub.asyncIterator([`USER_TYPING_${channelId}`])
}
}
}
const schema = makeExecutableSchema({ typeDefs, resolvers })
const httpServer = createServer(app)
const wsServer = new WebSocketServer({
server: httpServer,
path: '/graphql'
})
useServer({ schema }, wsServer)
httpServer.listen(4000)
import { PubSub } from 'graphql-subscriptions'
const pubsub = new PubSub()
const resolvers = {
Mutation: {
createMessage: async (_, { input }, { user }) => {
const message = await db.messages.create({
...input,
authorId: user.id
})
pubsub.publish('MESSAGE_CREATED', { messageCreated: message })
return message
}
}
}
const loggingPlugin = {
async requestDidStart(requestContext) {
console.log('Request started:', requestContext.request.query)
return {
async willSendResponse(requestContext) {
console.log('Response:', requestContext.response)
},
async didEncounterErrors(requestContext) {
console.error('Errors:', requestContext.errors)
}
}
}
}
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [loggingPlugin]
})
import { ApolloServerPluginUsageReporting } from '@apollo/server/plugin/usageReporting'
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [
ApolloServerPluginUsageReporting({
sendVariableValues: { all: true },
sendHeaders: { all: true }
})
]
})
npm install -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-resolvers
# codegen.yml
generates:
./src/generated/graphql.ts:
plugins:
- typescript
- typescript-resolvers
config:
contextType: ../context#Context
mappers:
User: ../models#UserModel
import type { Resolvers } from './generated/graphql'
const resolvers: Resolvers = {
Query: {
// Fully typed resolvers
user: (_, { id }) => db.users.findById(id)
}
}
This 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.