Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an example using Nexus to extend the Keystone schema #6792

Merged
merged 22 commits into from
Nov 11, 2021
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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