Skip to content

Commit

Permalink
Add an example using Nexus to extend the Keystone schema (keystonejs#…
Browse files Browse the repository at this point in the history
…6792)

* Add an example using Nexus to extend the Keystone schema

Co-authored-by: mitchellhamilton <mitchell@hamil.town>
  • Loading branch information
JedWatson and emmatown authored Nov 11, 2021
1 parent 4d05d1d commit 787de6f
Show file tree
Hide file tree
Showing 19 changed files with 616 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/three-beans-cross.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@keystone-next/example-extend-graphql-schema-nexus": patch
---

Added an example using Nexus to extend the Keystone schema
3 changes: 2 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ coverage
dist
reports
docs/public/assets/
tests/test-projects/live-reloading/schemas/syntax-error.js
nexus-typegen.ts
tests/test-projects/live-reloading/schemas/syntax-error.js
1 change: 1 addition & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ jobs:
'default-values.test.ts',
'extend-graphql-schema.test.ts',
'extend-graphql-schema-graphql-ts.test.ts',
'extend-graphql-schema-nexus.test.ts',
'json.test.ts',
'rest-api.test.ts',
'roles.test.ts',
Expand Down
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ coverage
docs/public/assets/
.keystone/tests
prisma-utils/src/generated
nexus-typegen.ts
tests/test-projects/live-reloading/schemas/syntax-error.js
4 changes: 3 additions & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ Each project below demonstrates a Keystone feature you can learn about and exper
- [`withAuth()`](./with-auth): Adds password-based authentication to the Task Manager base.
- [JSON field](./json): Adds a JSON field to the Task Manager base.
- [`defaultValue`](./default-values): Adds default values to the Blog base.
- [`extendGraphqlSchema`](./extend-graphql-schema): Extends the GraphQL API of the Task Manager base.
- [`extendGraphqlSchema`](./extend-graphql-schema): Extends the GraphQL API of the Blog base.
- [`extendGraphqlSchema` with graphql-ts](./extend-graphql-schema-graphql-ts): Extends the GraphQL API of the Blog base with [graphql-ts](https://github.com/Thinkmill/graphql-ts).
- [`extendGraphqlSchema` with Nexus](./extend-graphql-schema-nexus): Extends the GraphQL API of the Blog base with [Nexus](https://nexusjs.org/).
- [Virtual field](./virtual-field): Adds virtual fields to the Blog base.
- [Document field](./document-field): Adds document fields to the Blog base.
- [Testing](./testing): Adds tests with `@keystone-next/keystone/testing` to the `withAuth()` example.
Expand Down
1 change: 1 addition & 0 deletions examples/extend-graphql-schema-nexus/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nexus/nexus-typegen.ts
1 change: 1 addition & 0 deletions examples/extend-graphql-schema-nexus/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# @keystone-next/example-extend-graphql-schema
32 changes: 32 additions & 0 deletions examples/extend-graphql-schema-nexus/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
## Feature Example - Using Nexus to extend the GraphQL API

This project demonstrates how to use [Nexus](https://nexusjs.org) 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 integrate Nexus with Keystone and use it to extend the GraphQL API. For a simpler version without Nexus, see the [Extend GraphQL Schema](../extend-graphql-schema) example.

The Nexus schema is defined in [`/nexus`](./nexus/index.ts). It's loosely inspired by the [Nexus Tutorial](https://nexusjs.org/docs/getting-started/tutorial/chapter-setup-and-first-query)

The resulting schema is then merged into Keystone's schema using [`@graphql-tools/schema`](https://www.graphql-tools.com/docs/schema-merging#getting-started)

## Current Limitations

Ideally, we could tell Nexus about the GraphQL types Keystone generates so you could write Nexus fields that return Keystone types. Making this work requires more research, so the example currently creates a separate `NexusPost` type and uses Prisma to query posts from the database.

There's also a Prisma plugin for Nexus in development here: https://github.com/prisma/nexus-prisma

When it's ready, it would make a good addition to this example (showing how to integrate the Prisma plugin with the Keystone-generated Prisma schema to auto-generate Nexus schema)
13 changes: 13 additions & 0 deletions examples/extend-graphql-schema-nexus/keystone.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { config } from '@keystone-next/keystone';
import { mergeSchemas } from '@graphql-tools/schema';
import { lists } from './schema';
import { nexusSchema } from './nexus';

export default config({
db: {
provider: 'sqlite',
url: process.env.DATABASE_URL || 'file:./keystone-example.db',
},
lists,
extendGraphqlSchema: keystoneSchema => mergeSchemas({ schemas: [keystoneSchema, nexusSchema] }),
});
20 changes: 20 additions & 0 deletions examples/extend-graphql-schema-nexus/nexus/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import path from 'path';
import { makeSchema } from 'nexus';

import * as types from './types';

export const nexusSchema = makeSchema({
types,
outputs: {
typegen: path.join(process.cwd(), 'nexus', 'nexus-typegen.ts'),
},
// __dirname is absolute in Node but under webpack it is not so this is
// "only generate when running under webpack and not in production"
shouldGenerateArtifacts: !path.isAbsolute(__dirname) && process.env.NODE_ENV !== 'production',
// This binds the Keystone Context with correctly generated types for the
// Keystone `db` and `query` args, as well as the prisma client
contextType: {
module: path.join(process.cwd(), 'node_modules', '.keystone', 'types.d.ts'),
export: 'KeystoneContext',
},
});
46 changes: 46 additions & 0 deletions examples/extend-graphql-schema-nexus/nexus/types/Post.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/* -- NOTES --
This example demonstrates using the prisma client (from Keystone's context)
to return posts with a custom nexus-specified type.
Ideally, we could tell Nexus about the GraphQL schema Keystone generates and
not need to generate the output type again here, but that needs some more
research (and maybe a plugin for nexus?)
*/

import { extendType, intArg, list, stringArg, nonNull, objectType } from 'nexus';

export const NexusPost = objectType({
name: 'NexusPost',
definition(t) {
t.string('id');
t.string('title');
t.string('status');
t.string('content');
},
});

export const PostQuery = extendType({
type: 'Query',
definition(t) {
t.field('nexusPosts', {
type: nonNull(list('NexusPost')),
args: {
authorId: stringArg(),
days: nonNull(intArg({ default: 7 })),
},
async resolve(root, { authorId, days }, context) {
const cutoff = new Date(
new Date().setUTCDate(new Date().getUTCDate() - days)
).toISOString();

return await context.prisma.post.findMany({
where: {
...(authorId ? { author: { id: authorId } } : null),
publishDate: { gt: cutoff },
},
});
},
});
},
});
27 changes: 27 additions & 0 deletions examples/extend-graphql-schema-nexus/nexus/types/Thing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// This demonstrates the most basic Nexus usage to query a list of things

import { extendType, objectType } from 'nexus';

export const Thing = objectType({
name: 'Thing',
definition(t) {
t.int('id');
t.string('title');
},
});

export const ThingQuery = extendType({
type: 'Query',
definition(t) {
t.nonNull.list.field('things', {
type: 'Thing',
resolve() {
return [
{ id: 1, title: 'Keystone' },
{ id: 2, title: 'Prisma' },
{ id: 3, title: 'Nexus' },
];
},
});
},
});
2 changes: 2 additions & 0 deletions examples/extend-graphql-schema-nexus/nexus/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './Post';
export * from './Thing';
21 changes: 21 additions & 0 deletions examples/extend-graphql-schema-nexus/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "@keystone-next/example-extend-graphql-schema-nexus",
"version": "1.0.9",
"private": true,
"license": "MIT",
"scripts": {
"dev": "keystone-next dev",
"start": "keystone-next start",
"build": "keystone-next build"
},
"dependencies": {
"@graphql-tools/schema": "^8.3.1",
"@keystone-next/keystone": "^27.0.1",
"graphql": "^15.7.2",
"nexus": "^1.1.0"
},
"engines": {
"node": "^12.20 || >= 14.13"
},
"repository": "https://github.com/keystonejs/keystone/tree/main/examples/extend-graphql-schema-nexus"
}
Loading

0 comments on commit 787de6f

Please sign in to comment.