-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add extendGraphQLSchema example (#5820)
- Loading branch information
Showing
12 changed files
with
805 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@keystone-next/example-extend-graphql-schema': major | ||
--- | ||
|
||
Initial version of the `extend-graphql-schema` example. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# @keystone-next/example-extend-graphql-schema |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
## Feature Example - Extend GraphQL Schema | ||
|
||
This project demonstrates how to extend the GraphQL API provided by Keystone with custom queries and mutations. | ||
It builds on the [Blog](../blog) starter project. | ||
|
||
## Instructions | ||
|
||
To run this project, clone the Keystone repository locally then navigate to this directory and run: | ||
|
||
```shell | ||
yarn dev | ||
``` | ||
|
||
This will start the Admin UI at [localhost:3000](http://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](http://localhost:3000/api/graphql), which allows you to directly run GraphQL queries and mutations. | ||
|
||
## Features | ||
|
||
This project demonstrates how to extend the GraphQL API provided by Keystone with custom queries and mutations. | ||
Schema extensions are set using the [`extendGraphqlSchema`](https://next.keystonejs.com/apis/config#extend-graphql-schema) 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](https://www.apollographql.com/docs/apollo-server/schema/schema/) and [resolvers](https://www.apollographql.com/docs/apollo-server/data/resolvers/). | ||
|
||
### Custom mutation | ||
|
||
We add a custom mutation to our schema using `type Mutation` in the `typeDefs`, and defining `resolvers.Mutation`. | ||
|
||
```typescript | ||
extendGraphqlSchema: graphQLSchemaExtension({ | ||
typeDefs: ` | ||
type Mutation { | ||
""" Publish a post """ | ||
publishPost(id: ID!): Post | ||
}`, | ||
resolvers: { | ||
Mutation: { | ||
publishPost: (root, { id }, context) => { | ||
return context.db.lists.Post.updateOne({ | ||
id, | ||
data: { status: 'published', publishDate: new Date().toUTCString() }, | ||
}); | ||
}, | ||
}, | ||
}, | ||
}), | ||
``` | ||
|
||
### Custom query | ||
|
||
We add a custom query to our schema using `type Query` in the `typeDefs`, and defining `resolvers.Query`. | ||
|
||
```typescript | ||
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.lists.Post.findMany({ | ||
where: { author: { id }, publishDate_gt: cutoff }, | ||
}); | ||
}, | ||
}, | ||
}, | ||
}), | ||
``` | ||
|
||
### Custom type | ||
|
||
We add a custom type to our schema using `type Statisics` in the `typeDefs`, and defining `resolvers.Statisics`. | ||
|
||
```typescript | ||
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.lists.Post.count({ | ||
where: { author: { id }, status: 'draft' }, | ||
}); | ||
const published = await context.lists.Post.count({ | ||
where: { author: { id }, status: 'published' }, | ||
}); | ||
const { posts } = await context.lists.Author.findOne({ | ||
where: { id }, | ||
query: 'posts(first: 1, orderBy: { publishDate: desc }) { id }', | ||
}); | ||
return { draft, published, latestPostId: posts ? posts[0].id : null }; | ||
}, | ||
}, | ||
Statistics: { | ||
latest: (root, args, context) => | ||
context.db.lists.Post.findOne({ where: { id: root.latestPostId } }), | ||
}, | ||
}, | ||
}), | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { graphQLSchemaExtension } from '@keystone-next/keystone/schema'; | ||
|
||
export const extendGraphqlSchema = graphQLSchemaExtension({ | ||
typeDefs: ` | ||
type Mutation { | ||
""" Publish a post """ | ||
publishPost(id: ID!): Post | ||
} | ||
type Query { | ||
""" Return all posts for a user from the last <days> days """ | ||
recentPosts(id: ID!, days: Int! = 7): [Post] | ||
""" 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: { | ||
Mutation: { | ||
publishPost: (root, { id }, context) => { | ||
// Note we use `context.db.lists.Post` here as we have a return type | ||
// of Post, and this API provides results in the correct format. | ||
// If you accidentally use `context.lists.Post` here you can expect problems | ||
// when accessing the fields in your GraphQL client. | ||
return context.db.lists.Post.updateOne({ | ||
id, | ||
data: { status: 'published', publishDate: new Date().toUTCString() }, | ||
}); | ||
}, | ||
}, | ||
Query: { | ||
recentPosts: (root, { id, days }, context) => { | ||
// Create a date string <days> in the past from now() | ||
const cutoff = new Date( | ||
new Date().setUTCDate(new Date().getUTCDate() - days) | ||
).toUTCString(); | ||
|
||
// Note we use `context.db.lists.Post` here as we have a return type | ||
// of [Post], and this API provides results in the correct format. | ||
// If you accidentally use `context.lists.Post` here you can expect problems | ||
// when accessing the fields in your GraphQL client. | ||
return context.db.lists.Post.findMany({ | ||
where: { author: { id }, publishDate_gt: cutoff }, | ||
}); | ||
}, | ||
stats: async (root, { id }, context) => { | ||
const draft = await context.lists.Post.count({ | ||
where: { author: { id }, status: 'draft' }, | ||
}); | ||
const published = await context.lists.Post.count({ | ||
where: { author: { id }, status: 'published' }, | ||
}); | ||
const { posts } = await context.lists.Author.findOne({ | ||
where: { id }, | ||
query: 'posts(first: 1, orderBy: { publishDate: desc }) { id }', | ||
}); | ||
return { draft, published, latestPostId: posts ? posts[0].id : null }; | ||
}, | ||
}, | ||
Statistics: { | ||
// The stats resolver returns an object which is passed to this resolver as | ||
// the root value. We use that object to further resolve ths specific fields. | ||
// In this case we want to take root.latestPostId and resolve it as a Post object | ||
// | ||
// As above we use the context.db.lists.Post API to achieve this. | ||
latest: (root, args, context) => | ||
context.db.lists.Post.findOne({ where: { id: root.latestPostId } }), | ||
// We don't need to define resolvers for draft and published, as apollo will | ||
// return root.draft and root.published respectively. | ||
}, | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { config } from '@keystone-next/keystone/schema'; | ||
import { lists } from './schema'; | ||
import { extendGraphqlSchema } from './custom-schema'; | ||
|
||
export default config({ | ||
db: { | ||
provider: 'sqlite', | ||
url: process.env.DATABASE_URL || 'file:./keystone-example.db', | ||
}, | ||
lists, | ||
extendGraphqlSchema, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
{ | ||
"name": "@keystone-next/example-extend-graphql-schema", | ||
"version": "0.0.0", | ||
"private": true, | ||
"license": "MIT", | ||
"scripts": { | ||
"dev": "keystone-next dev", | ||
"start": "keystone-next start", | ||
"build": "keystone-next build" | ||
}, | ||
"dependencies": { | ||
"@keystone-next/fields": "^9.0.0", | ||
"@keystone-next/keystone": "^18.0.0" | ||
}, | ||
"devDependencies": { | ||
"typescript": "^4.2.4" | ||
}, | ||
"engines": { | ||
"node": "^12.20 || >= 14.13" | ||
}, | ||
"repository": "https://github.com/keystonejs/keystone/tree/master/examples/extend-graphql-schema" | ||
} |
Oops, something went wrong.
8598c83
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs: