GraphQL Implementation Overview
Implement GraphQL APIs with proper schema design, resolver patterns, error handling, and performance optimization for flexible client-server communication.
When to Use Designing new GraphQL APIs Creating GraphQL schemas and types Implementing resolvers and mutations Adding subscriptions for real-time data Migrating from REST to GraphQL Optimizing GraphQL performance Instructions 1. GraphQL Schema Design type User { id: ID! email: String! firstName: String! lastName: String! role: UserRole! posts: [Post!]! createdAt: DateTime! updatedAt: DateTime! }
enum UserRole { ADMIN USER MODERATOR }
type Post { id: ID! title: String! content: String! author: User! comments: [Comment!]! publishedAt: DateTime createdAt: DateTime! }
type Comment { id: ID! text: String! author: User! post: Post! createdAt: DateTime! }
type Query { user(id: ID!): User users(limit: Int, offset: Int): [User!]! post(id: ID!): Post posts(authorId: ID, limit: Int, offset: Int): [Post!]! search(query: String!): [SearchResult!]! }
union SearchResult = User | Post | Comment
type Mutation { createUser(input: CreateUserInput!): User! updateUser(id: ID!, input: UpdateUserInput!): User! deleteUser(id: ID!): Boolean! createPost(input: CreatePostInput!): Post! updatePost(id: ID!, input: UpdatePostInput!): Post! deletePost(id: ID!): Boolean! createComment(postId: ID!, text: String!): Comment! }
input CreateUserInput { email: String! firstName: String! lastName: String! role: UserRole! }
input UpdateUserInput { email: String firstName: String lastName: String role: UserRole }
input CreatePostInput { title: String! content: String! }
input UpdatePostInput { title: String content: String publishedAt: DateTime }
type Subscription { userCreated: User! postPublished: Post! commentAdded(postId: ID!): Comment! }
scalar DateTime
- Node.js Apollo Server Implementation const { ApolloServer, gql } = require('apollo-server-express'); const express = require('express');
const typeDefs = gql` type Query { user(id: ID!): User users: [User!]! }
type User { id: ID! email: String! firstName: String! lastName: String! posts: [Post!]! }
type Post { id: ID! title: String! content: String! author: User! }
type Mutation { createUser(email: String!, firstName: String!, lastName: String!): User! createPost(title: String!, content: String!): Post! } `;
const resolvers = { Query: { user: async (, { id }, { db }) => { return db.users.findById(id); }, users: async (, __, { db }) => { return db.users.findAll(); } },
User: { posts: async (user, _, { db }) => { return db.posts.findByAuthorId(user.id); } },
Post: { author: async (post, _, { db }) => { return db.users.findById(post.authorId); } },
Mutation: { createUser: async (, { email, firstName, lastName }, { db }) => { const user = { id: Date.now().toString(), email, firstName, lastName }; db.users.save(user); return user; }, createPost: async (, { title, content }, { user, db }) => { if (!user) throw new Error('Unauthorized'); const post = { id: Date.now().toString(), title, content, authorId: user.id }; db.posts.save(post); return post; } } };
const server = new ApolloServer({ typeDefs, resolvers, context: ({ req }) => ({ user: req.user, db: require('./database') }) });
const app = express(); server.start().then(() => { server.applyMiddleware({ app }); app.listen(4000, () => console.log('GraphQL server running on port 4000')); });
- Python GraphQL Implementation (Graphene) import graphene from datetime import datetime from typing import List
class UserType(graphene.ObjectType): id = graphene.ID(required=True) email = graphene.String(required=True) first_name = graphene.String(required=True) last_name = graphene.String(required=True) posts = graphene.List(lambda: PostType)
class PostType(graphene.ObjectType): id = graphene.ID(required=True) title = graphene.String(required=True) content = graphene.String(required=True) author = graphene.Field(UserType) created_at = graphene.DateTime(required=True)
class Query(graphene.ObjectType): user = graphene.Field(UserType, id=graphene.ID(required=True)) users = graphene.List(UserType) posts = graphene.List(PostType, author_id=graphene.ID())
def resolve_user(self, info, id):
return User.objects.get(pk=id)
def resolve_users(self, info):
return User.objects.all()
def resolve_posts(self, info, author_id=None):
if author_id:
return Post.objects.filter(author_id=author_id)
return Post.objects.all()
class CreateUserMutation(graphene.Mutation): class Arguments: email = graphene.String(required=True) first_name = graphene.String(required=True) last_name = graphene.String(required=True)
user = graphene.Field(UserType)
success = graphene.Boolean()
def mutate(self, info, email, first_name, last_name):
user = User.objects.create(
email=email,
first_name=first_name,
last_name=last_name
)
return CreateUserMutation(user=user, success=True)
class Mutation(graphene.ObjectType): create_user = CreateUserMutation.Field()
schema = graphene.Schema(query=Query, mutation=Mutation)
- Query Examples
Get user with posts
query GetUserWithPosts { user(id: "123") { id email firstName posts { id title createdAt } } }
Paginated users query
query GetUsers($limit: Int, $offset: Int) { users(limit: $limit, offset: $offset) { id email firstName } }
Search across types
query Search($query: String!) { search(query: $query) { ... on User { id email } ... on Post { id title } } }
Create user mutation
mutation CreateUser($input: CreateUserInput!) { createUser(input: $input) { id email firstName } }
Subscribe to new comments
subscription OnCommentAdded($postId: ID!) { commentAdded(postId: $postId) { id text author { firstName } } }
- Error Handling const resolvers = { Query: { user: async (_, { id }) => { try { const user = await User.findById(id); if (!user) { throw new GraphQLError('User not found', { extensions: { code: 'NOT_FOUND', userId: id } }); } return user; } catch (error) { throw new GraphQLError('Database error', { originalError: error, extensions: { code: 'INTERNAL_ERROR' } }); } } } };
server.formatError = (formattedError) => ({ message: formattedError.message, code: formattedError.extensions?.code || 'INTERNAL_ERROR', timestamp: new Date().toISOString() });
Best Practices ✅ DO Use clear, descriptive field names Design schemas around client needs Implement proper error handling Use input types for mutations Add subscriptions for real-time data Cache resolvers efficiently Validate input data Use federation for scalability ❌ DON'T Over-nest queries deeply Expose internal database IDs Return sensitive data without authorization Create overly complex schemas Forget to handle null values Ignore N+1 query problems Skip error messages Performance Tips Use DataLoader to batch database queries Implement query complexity analysis Cache at resolver level Use connection patterns for pagination Monitor query execution time