Exercises
Three short GraphQL exercises - schema, query, and resolver.
Three short challenges
EXAMPLE
# 1. Design a schema for a small blog
# Requirements: posts, comments, authors, tags. Pagination by cursor.
scalar DateTime
type Author {
id: ID!
email: String!
posts(first: Int = 10, after: String): PostConnection!
}
type Tag {
id: ID!
name: String!
}
type Post {
id: ID!
title: String!
body: String!
author: Author!
tags: [Tag!]!
comments(first: Int = 10, after: String): CommentConnection!
publishedAt: DateTime
}
type Comment {
id: ID!
body: String!
author: Author!
createdAt: DateTime!
}
type PostConnection {
edges: [PostEdge!]!
pageInfo: PageInfo!
}
type PostEdge { node: Post!; cursor: String! }
type CommentConnection {
edges: [CommentEdge!]!
pageInfo: PageInfo!
}
type CommentEdge { node: Comment!; cursor: String! }
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
type Query {
post(id: ID!): Post
posts(first: Int = 10, after: String, tag: String): PostConnection!
}
# 2. Query: first page of posts tagged 'graphql', plus 5 comments each
query Feed($tag: String!, $count: Int = 5) {
posts(first: 10, tag: $tag) {
edges {
node {
id
title
author { email }
comments(first: $count) {
edges { node { id body author { email } } }
}
}
}
pageInfo { hasNextPage endCursor }
}
}
# 3. Resolver: implement post.comments efficiently with DataLoader to avoid N+1
// resolvers.ts
import DataLoader from 'dataloader';
function makeCommentsByPostLoader(db) {
return new DataLoader<string, Comment[]>(async (postIds) => {
const rows = await db.comments.findMany({
where: { postId: { in: [...postIds] } },
orderBy: { createdAt: 'asc' },
});
const grouped = new Map<string, Comment[]>(postIds.map((id) => [id, []]));
for (const r of rows) grouped.get(r.postId)!.push(r);
return postIds.map((id) => grouped.get(id)!);
});
}
export const resolvers = {
Post: {
comments: async (post, args, ctx) => {
const all = await ctx.loaders.commentsByPost.load(post.id);
return paginate(all, args);
},
},
};
// Per-request loader
function createContext(req) {
return { loaders: { commentsByPost: makeCommentsByPostLoader(db) } };
}
# Stretch
# - Add a mutation createPost with a payload type
# - Add a Subscription postCreated
# - Add a UserError type so 'duplicate title' is not a top-level error
Why it matters
Schema design, querying with variables, and DataLoader resolvers are the three legs of GraphQL muscle memory. Drill them on a small domain like a blog before you scale up.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Discussion
Loading…