This project demonstrates how to extend the GraphQL API provided by Keystone with custom queries and mutations. It builds on the Blog starter project.
To run this project, clone the Keystone repository locally, run yarn
at the root of the repository then navigate to this directory and run:
yarn dev
This will start the Admin UI at localhost:3000. You can use the Admin UI to create items in your database.
You can also access a GraphQL Playground at localhost:3000/api/graphql, which allows you to directly run GraphQL queries and mutations.
This project demonstrates how to extend the GraphQL API provided by Keystone with custom queries and mutations.
Schema extensions are set using the extendGraphqlSchema
config option.
The function graphQLSchemaExtension
accepts a typeDefs
string, which lets you define your GraphQL types, and a resolvers
object, which lets your define resolvers for your types.
The Apollo docs contain more information on GraphQL types and resolvers.
We add a custom mutation to our schema using type Mutation
in the typeDefs
, and defining resolvers.Mutation
.
extendGraphqlSchema: graphQLSchemaExtension({
typeDefs: `
type Mutation {
""" Publish a post """
publishPost(id: ID!): Post
}`,
resolvers: {
Mutation: {
publishPost: (root, { id }, context) => {
return context.db.Post.updateOne({
id,
data: { status: 'published', publishDate: new Date().toUTCString() },
});
},
},
},
}),
We add a custom query to our schema using type Query
in the typeDefs
, and defining resolvers.Query
.
extendGraphqlSchema: graphQLSchemaExtension({
typeDefs: `
type Query {
""" Return all posts for a user from the last <days> days """
recentPosts(id: ID!, days: Int! = 7): [Post]
}`,
resolvers: {
Query: {
recentPosts: (root, { id, days }, context) => {
const cutoff = new Date(
new Date().setUTCDate(new Date().getUTCDate() - days)
).toUTCString();
return context.db.Post.findMany({
where: { author: { id }, publishDate_gt: cutoff },
});
},
},
},
}),
We add a custom type to our schema using type Statisics
in the typeDefs
, and defining resolvers.Statisics
.
extendGraphqlSchema: graphQLSchemaExtension({
typeDefs: `
type Query {
""" Compute statistics for a user """
stats(id: ID!): Statistics
}
""" A custom type to represent statistics for a user """
type Statistics {
draft: Int
published: Int
latest: Post
}`,
resolvers: {
Query: {
stats: async (root, { id }, context) => {
const draft = await context.query.Post.count({
where: { author: { id }, status: 'draft' },
});
const published = await context.query.Post.count({
where: { author: { id }, status: 'published' },
});
const { posts } = await context.query.Author.findOne({
where: { id },
query: 'posts(take: 1, orderBy: { publishDate: desc }) { id }',
});
return { draft, published, latestPostId: posts ? posts[0].id : null };
},
},
Statistics: {
latest: (root, args, context) =>
context.db.Post.findOne({ where: { id: root.latestPostId } }),
},
},
}),