2024-09-02
Build efficient, type-safe APIs with GraphQL, including schema design, resolvers, and performance optimization.
GraphQL revolutionizes API development with its query language and type system. This guide covers schema design, resolver implementation, authentication, performance optimization, and best practices for production GraphQL APIs.
✓ Request exactly what you need
✓ Single endpoint for all queries
✓ Strong type system
✓ Real-time subscriptions
✓ Self-documenting API
✓ No over/under-fetching
✓ Simple HTTP caching
✓ File uploads straightforward
✓ Mature ecosystem
✓ Better for public APIs
✓ Simpler learning curve
✓ URL-based resources
# Type definitions
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
createdAt: DateTime!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
comments: [Comment!]!
published: Boolean!
tags: [String!]!
}
type Query {
user(id: ID!): User
users(limit: Int = 10, offset: Int = 0): [User!]!
post(id: ID!): Post
searchPosts(query: String!): [Post!]!
}
type Mutation {
createUser(input: CreateUserInput!): User!
updatePost(id: ID!, input: UpdatePostInput!): Post!
deletePost(id: ID!): Boolean!
}
type Subscription {
postAdded: Post!
commentAdded(postId: ID!): Comment!
}
const resolvers = {
Query: {
user: async (parent, { id }, context) => {
return await context.dataSources.userAPI.getUser(id);
},
users: async (parent, { limit, offset }, context) => {
return await context.dataSources.userAPI.getUsers({ limit, offset });
},
searchPosts: async (parent, { query }, context) => {
return await context.dataSources.postAPI.search(query);
}
},
Mutation: {
createUser: async (parent, { input }, context) => {
// Check authentication
if (!context.user) {
throw new AuthenticationError('Must be logged in');
}
return await context.dataSources.userAPI.create(input);
}
},
User: {
posts: async (user, args, context) => {
// N+1 query solution with DataLoader
return await context.loaders.postsByUser.load(user.id);
}
}
};
# Request only needed fields
query GetUser {
user(id: "123") {
name
posts {
title
published
}
}
}
# With variables
query GetUser($userId: ID!) {
user(id: $userId) {
...UserFields
}
}
fragment UserFields on User {
id
name
}
mutation CreatePost($input: CreatePostInput!) {
createPost(input: $input) {
id
title
author {
name
}
}
}
# Variables
{
"input": {
"title": "GraphQL Best Practices",
"content": "...",
"published": true
}
}
const DataLoader = require('dataloader');
// Batch and cache database queries
const createLoaders = () => ({
postsByUser: new DataLoader(async (userIds) => {
const posts = await Post.find({
userId: { $in: userIds }
});
// Group posts by user ID
const postsByUser = userIds.map(userId =>
posts.filter(post => post.userId === userId)
);
return postsByUser;
}),
userById: new DataLoader(async (ids) => {
const users = await User.find({ _id: { $in: ids } });
return ids.map(id => users.find(user => user.id === id));
})
});
Prevent expensive nested queries:
const depthLimit = require('graphql-depth-limit');
const server = new ApolloServer({
typeDefs,
resolvers,
validationRules: [depthLimit(5)], // Max depth of 5
formatError: (err) => {
// Remove stack trace in production
delete err.extensions.exception.stacktrace;
return err;
}
});
Error Handling
Use custom error classes and provide meaningful error messages with proper error codes.
Pagination
Implement cursor-based pagination for large datasets instead of offset-based.
Caching
Use Apollo Client cache and implement server-side caching with Redis for expensive queries.
Published on 2024-09-02 • Category: Backend
← Back to BlogFree online developer tools and utilities for encoding, formatting, generating, and analyzing data. No registration required - all tools work directly in your browser.
Built for developers, by developers. Privacy-focused and open source.
Free online tools for Base64 encoding, JSON formatting, URL encoding, hash generation, UUID creation, QR codes, JWT decoding, timestamp conversion, regex testing, and more.
© 2024 NarvikHub. All rights reserved.