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

Update @graphql-ts/schema, rename schema export to graphql and move it to main entrypoint #6393

Merged
merged 3 commits into from
Aug 23, 2021
Merged
Show file tree
Hide file tree
Changes from all 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/happy-chairs-juggle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@keystone-next/keystone': major
---

Updated `@graphql-ts/schema` to `0.3.0` and moved the `schema` export to `@keystone-next/keystone` entrypoint and renamed it to `graphql`. `bindSchemaAPIToContext` on the `graphql` export has also been renamed to `bindGraphQLSchemaAPIToContext`.
9 changes: 4 additions & 5 deletions docs/pages/docs/apis/fields.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -562,18 +562,17 @@ Options:
- `graphQLReturnFragment` (default: `''` ): The sub-fields that should be fetched by the Admin UI when displaying this field.

```typescript
import { config, createSchema, list } from '@keystone-next/keystone';
import { config, createSchema, list, graphql } from '@keystone-next/keystone';
import { virtual } from '@keystone-next/keystone/fields';
import { schema } from '@keystone-next/keystone/types';

export default config({
lists: createSchema({
ListName: list({
fields: {
fieldName: virtual({
field: schema.field({
type: schema.String,
args: { something: schema.arg({ type: schema.Int }) },
field: graphql.field({
type: graphql.String,
args: { something: graphql.arg({ type: graphql.Int }) },
resolve(item, args, context, info) {

}
Expand Down
34 changes: 17 additions & 17 deletions docs/pages/docs/guides/custom-fields.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
FieldTypeFunc,
CommonFieldConfig,
fieldType,
schema,
graphql,
orderDirectionEnum,
filters,
FieldDefaultValue,
Expand Down Expand Up @@ -58,12 +58,12 @@ export const myInt =
})({
...config,
input: {
where: { arg: schema.arg({ type: filters[meta.provider].Int.optional }), resolve: filters.resolveCommon },
create: { arg: schema.arg({ type: schema.Int }) },
update: { arg: schema.arg({ type: schema.Int }) },
orderBy: { arg: schema.arg({ type: orderDirectionEnum }) },
where: { arg: graphql.arg({ type: filters[meta.provider].Int.optional }), resolve: filters.resolveCommon },
create: { arg: graphql.arg({ type: graphql.Int }) },
update: { arg: graphql.arg({ type: graphql.Int }) },
orderBy: { arg: graphql.arg({ type: orderDirectionEnum }) },
},
output: schema.field({ type: schema.Int }),
output: graphql.field({ type: graphql.Int }),
views: require.resolve('./view.tsx'),
__legacy: {
isRequired,
Expand All @@ -83,21 +83,21 @@ The `input` object defines the GraphQL inputs for the field type.

```ts
input: {
where: { arg: schema.arg({ type: filters[meta.provider].Int.optional }), resolve: filters.resolveCommon },
create: { arg: schema.arg({ type: schema.Int }) },
update: { arg: schema.arg({ type: schema.Int }) },
orderBy: { arg: schema.arg({ type: orderDirectionEnum }) },
where: { arg: graphql.arg({ type: filters[meta.provider].Int.optional }), resolve: filters.resolveCommon },
create: { arg: graphql.arg({ type: graphql.Int }) },
update: { arg: graphql.arg({ type: graphql.Int }) },
orderBy: { arg: graphql.arg({ type: orderDirectionEnum }) },
},
```

You can also provide resolvers to transform the value coming from GraphQL into the value that is passed to Prisma.

```ts
input: {
where: { arg: schema.arg({ type: filters[meta.provider].Int.optional }), resolve: filters.resolveCommon },
create: { arg: schema.arg({ type: schema.Int }), resolve: (val, context) => val },
update: { arg: schema.arg({ type: schema.Int }), resolve: (val, context) => val },
orderBy: { arg: schema.arg({ type: orderDirectionEnum }), resolve: (val, context) => val },
where: { arg: graphql.arg({ type: filters[meta.provider].Int.optional }), resolve: filters.resolveCommon },
create: { arg: graphql.arg({ type: graphql.Int }), resolve: (val, context) => val },
update: { arg: graphql.arg({ type: graphql.Int }), resolve: (val, context) => val },
orderBy: { arg: graphql.arg({ type: orderDirectionEnum }), resolve: (val, context) => val },
},
```

Expand All @@ -106,14 +106,14 @@ input: {
The output field defines what can be fetched from the field:

```ts
output: schema.field({ type: schema.Int })
output: graphql.field({ type: graphql.Int })
```

A resolver can also be provided:

```ts
output: schema.field({
type: schema.Int,
output: graphql.field({
type: graphql.Int,
resolve({ value, item }, args, context, info) {
return value;
}
Expand Down
59 changes: 29 additions & 30 deletions docs/pages/docs/guides/virtual-fields.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,16 @@ In this guide we'll introduce the syntax for adding virtual fields, and show how
We'll start with a list called `Example` and create a virtual field called `hello`.

```typescript
import { config, createSchema, list } from '@keystone-next/keystone';
import { config, createSchema, list, graphql } from '@keystone-next/keystone';
import { virtual } from '@keystone-next/keystone/fields';
import { schema } from '@keystone-next/keystone/types';

export default config({
lists: createSchema({
Example: list({
fields: {
hello: virtual({
field: schema.field({
type: schema.String,
field: graphql.field({
type: graphql.String,
resolve() {
return "Hello, world!";
},
Expand Down Expand Up @@ -57,18 +56,18 @@ which gives the response:

The value of `hello` is generated from the `resolve` function, which returns the string `"Hello, world!"`.

## The schema API
## The `graphql` API

The `virtual` field is configured using functions from the `schema` API in the `@keystone-next/keystone/types` package.
The `virtual` field is configured using functions from the `graphql` export from `@keystone-next/keystone`.
This API provides the interface required to create type-safe extensions to the Keystone GraphQL schema.
The schema API is based on the [`@graphql-ts/schema`](https://github.com/Thinkmill/graphql-ts) package.
The `graphql` API is based on the [`@graphql-ts/schema`](https://github.com/Thinkmill/graphql-ts) package.

The `virtual` field accepts a configuration option called `field`, which is a `schema.field()` object.
The `virtual` field accepts a configuration option called `field`, which is a `graphql.field()` object.

In our example we passed in two required options to `schema.field()`.
The option `type: schema.String` specifies the GraphQL type of our virtual field, and `resolve() { ... }` defines the [GraphQL resolver](https://graphql.org/learn/execution/#root-fields-resolvers) to be executed when this field is queried.
In our example we passed in two required options to `graphql.field()`.
The option `type: graphql.String` specifies the GraphQL type of our virtual field, and `resolve() { ... }` defines the [GraphQL resolver](https://graphql.org/learn/execution/#root-fields-resolvers) to be executed when this field is queried.

The `schema` API provides support for the built in GraphQL scalar types `Int`, `Float`, `String`, `Boolean`, and `ID`, as well as the Keystone custom scalars `Upload` and `JSON`.
The `graphql` API provides support for the built in GraphQL scalar types `Int`, `Float`, `String`, `Boolean`, and `ID`, as well as the Keystone custom scalars `Upload` and `JSON`.

## Resolver arguments

Expand All @@ -92,8 +91,8 @@ export default config({
content: text(),
author: relationship({ ref: 'Author', many: false }),
authorName: virtual({
type: schema.String,
field: schema.field({
type: graphql.String,
field: graphql.field({
async resolve(item, args, context) {
const { author } = await context.lists.Post.findOne({
where: { id: item.id.toString() },
Expand Down Expand Up @@ -130,11 +129,11 @@ export default config({
fields: {
content: text(),
excerpt: virtual({
field: schema.field({
type: schema.String,
field: graphql.field({
type: graphql.String,
args: {
length: schema.arg({
type: schema.nonNull(schema.Int),
length: graphql.arg({
type: graphql.nonNull(graphql.Int),
defaultValue: 200
}),
},
Expand Down Expand Up @@ -188,7 +187,7 @@ Had we not specified `defaultValue` in our field, the `graphQLReturnFragment` ar
The examples above returned a scalar `String` value. Virtual fields can also be configured to return a GraphQL object.

In our blog example we might want to provide some statistics on each blog post, such as the number of words, sentences, and paragraphs in the post.
We can set up a GraphQL type called `PostCounts` to represent this data using the `schema.object()` function.
We can set up a GraphQL type called `PostCounts` to represent this data using the `graphql.object()` function.

```typescript
export default config({
Expand All @@ -197,17 +196,17 @@ export default config({
fields: {
content: text(),
counts: virtual({
field: schema.field({
type: schema.object<{
field: graphql.field({
type: graphql.object<{
words: number;
sentences: number;
paragraphs: number;
}>()({
name: 'PostCounts',
fields: {
words: schema.field({ type: schema.Int }),
sentences: schema.field({ type: schema.Int }),
paragraphs: schema.field({ type: schema.Int }),
words: graphql.field({ type: graphql.Int }),
sentences: graphql.field({ type: graphql.Int }),
paragraphs: graphql.field({ type: graphql.Int }),
},
}),
resolve(item: any) {
Expand Down Expand Up @@ -235,18 +234,18 @@ This fragment tells the Keystone Admin UI which values to show in the item page

#### Self-referencing objects

!> This information is specifically for TypeScript users of the `schema.object()` function with a self-referential GraphQL type.
!> This information is specifically for TypeScript users of the `graphql.object()` function with a self-referential GraphQL type.

GraphQL types will often contain references to themselves and to make TypeScript allow that, you need have an explicit type annotation of `schema.ObjectType<RootVal>` along with making `fields` a function that returns the object.
GraphQL types will often contain references to themselves and to make TypeScript allow that, you need have an explicit type annotation of `graphql.ObjectType<RootVal>` along with making `fields` a function that returns the object.

```ts
type PersonRootVal = { name: string; friends: PersonRootVal[] };

const Person: schema.ObjectType<PersonRootVal> = schema.object<PersonRootVal>()({
const Person: graphql.ObjectType<PersonRootVal> = graphql.object<PersonRootVal>()({
name: "Person",
fields: () => ({
name: schema.field({ type: schema.String }),
friends: schema.field({ type: schema.list(Person) }),
name: graphql.field({ type: graphql.String }),
friends: graphql.field({ type: graphql.list(Person) }),
}),
});
```
Expand All @@ -256,7 +255,7 @@ const Person: schema.ObjectType<PersonRootVal> = schema.object<PersonRootVal>()(
Rather than returning a custom GraphQL object, we might want to have a virtual field which returns one of the GraphQL types generated by Keystone itself.
For example, for each `Author` we might want to return their `latestPost` as a `Post` object.

To achieve this, rather than passing in `schema.field({ ... })` as the `field` option, we pass in a function `lists => schema.field({ ... })`.
To achieve this, rather than passing in `graphql.field({ ... })` as the `field` option, we pass in a function `lists => graphql.field({ ... })`.
The argument `lists` contains the type information for all of the Keystone lists.
In our case, we want the output type of the `Post` list, so we specify `type: lists.Post.types.output`.

Expand All @@ -277,7 +276,7 @@ export const lists = createSchema({
posts: relationship({ ref: 'Post.author', many: true }),
latestPost: virtual({
field: lists =>
schema.field({
graphql.field({
type: lists.Post.types.output,
async resolve(item, args, context) {
const { posts } = await context.lists.Author.findOne({
Expand Down
10 changes: 5 additions & 5 deletions examples-staging/basic/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
} from '@keystone-next/keystone/fields';
import { document } from '@keystone-next/fields-document';
// import { cloudinaryImage } from '@keystone-next/cloudinary';
import { KeystoneListsAPI, schema } from '@keystone-next/keystone/types';
import { KeystoneListsAPI, graphql } from '@keystone-next/keystone/types';
import { componentBlocks } from './admin/fieldViews/Content';
import { KeystoneListsTypeInfo } from '.keystone/types';

Expand Down Expand Up @@ -84,8 +84,8 @@ export const lists = createSchema({
}),
posts: relationship({ ref: 'Post.author', many: true }),
randomNumber: virtual({
field: schema.field({
type: schema.Float,
field: graphql.field({
type: graphql.Float,
resolve() {
return randomNumber();
},
Expand All @@ -100,8 +100,8 @@ export const lists = createSchema({
},
fields: {
label: virtual({
field: schema.field({
type: schema.String,
field: graphql.field({
type: graphql.String,
resolve(item) {
return `${item.type} - ${item.value}`;
},
Expand Down
6 changes: 3 additions & 3 deletions examples-staging/ecommerce/schemas/Order.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { integer, text, relationship, virtual } from '@keystone-next/keystone/fields';
import { list } from '@keystone-next/keystone';
import { schema } from '@keystone-next/keystone/types';
import { graphql } from '@keystone-next/keystone/types';
import { isSignedIn, rules } from '../access';
import formatMoney from '../lib/formatMoney';

Expand All @@ -13,8 +13,8 @@ export const Order = list({
},
fields: {
label: virtual({
field: schema.field({
type: schema.String,
field: graphql.field({
type: graphql.String,
resolve(item) {
return `${formatMoney((item as any).total)}`;
},
Expand Down
14 changes: 7 additions & 7 deletions examples/custom-field/stars-field/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
FieldTypeFunc,
CommonFieldConfig,
orderDirectionEnum,
schema,
graphql,
filters,
} from '@keystone-next/keystone/types';

Expand Down Expand Up @@ -57,11 +57,11 @@ export const stars =
// all of these inputs are optional if they don't make sense for a particular field type
input: {
where: {
arg: schema.arg({ type: filters[meta.provider].Int.optional }),
arg: graphql.arg({ type: filters[meta.provider].Int.optional }),
resolve: filters.resolveCommon,
},
create: {
arg: schema.arg({ type: schema.Int }),
arg: graphql.arg({ type: graphql.Int }),
// this field type doesn't need to do anything special
// but field types can specify resolvers for inputs like they can for their output GraphQL field
// this function can be omitted, it is here purely to show how you could change it
Expand All @@ -82,12 +82,12 @@ export const stars =
return val;
},
},
update: { arg: schema.arg({ type: schema.Int }) },
orderBy: { arg: schema.arg({ type: orderDirectionEnum }) },
update: { arg: graphql.arg({ type: graphql.Int }) },
orderBy: { arg: graphql.arg({ type: orderDirectionEnum }) },
},
// this
output: schema.field({
type: schema.Int,
output: graphql.field({
type: graphql.Int,
// like the input resolvers, providing the resolver is unnecessary if you're just returning the value
// it is shown here to show what you could do
// eslint-disable-next-line @typescript-eslint/no-unused-vars
Expand Down
Loading