Skip to content

Commit

Permalink
feat: .nonNull / .nullable chaining, additional cleanup (#655)
Browse files Browse the repository at this point in the history
- Fixes SDL conversion
- Defer wrapping until types are built
- Supports GraphQL types anywhere that a type can be used, wrapped, etc.
- Split out the wrapping, w/ `finalizeWrapping` helper which adds in the `NonNull` to the list as needed
- Make `nonNull(nonNull(T))` & `nullable(nullable(T))` no-op for simplicity
- Change: `nullable(nonNull(T))` will undo the `nonNull`
- Properly type-check's `subscriptionType` by passing `Event` generic properly
- Adds `declarativeWrappingPlugin` for previous `nullable` / `list` behavior

Co-authored-by: Flavian DESVERNE <flavian.desverne@epitech.eu>
  • Loading branch information
tgriesser and Weakky committed Nov 23, 2020
1 parent 618521d commit 306dbaa
Show file tree
Hide file tree
Showing 54 changed files with 3,825 additions and 1,365 deletions.
36 changes: 6 additions & 30 deletions docs/content/015-api/060-args.mdx
Expand Up @@ -23,42 +23,18 @@ function requiredInt(opts: core.ScalarArgConfig<number>) {

Common options available for `arg` include the following:

**Required**

Whether the argument is required or not.

Format: `required?: boolean;`

Note, when `required: true`, `nullable: false`

&nbsp;

**Nullable**

Whether the argument is nullable or not.

Format: `nullable?: boolean;`

Note, when `nullable: true`, `required: false`

&nbsp;

**List**

Whether the argument is a list or not.

Format: `list?: null | true | boolean[];`

null = not a list
**Description**

true = list
The description to annotate the GraphQL SDL

array = nested list, where true/false decides whether the list member can be nullable
Format: `description?: string | null;`

&nbsp;

**Description**
**Default**

The description to annotate the GraphQL SDL
The default value for an argument

Format: `description?: string | null;`
Example: `intArg({ default: 1 })`
@@ -1,5 +1,5 @@
---
title: list / nonNull / nullable
title: list / nonNull
codeStyle: true
---

Expand Down Expand Up @@ -33,13 +33,10 @@ Below are some usage examples of `list()`:

| Example | GraphQL Type (nonNullDefaults = false) | GraphQL Type (nonNullDefaults = true) |
| ---------------------------------- | -------------------------------------- | ------------------------------------- |
| `nonNull('String')` | `String!` | `String!` |
| `nonNull(list('String'))` | `[String]!` | `[String!]!` |
| `list(nonNull('String'))` | `[String!]` | `[String!]!` |
| `list(list('String'))` | `[[String]]` | `[[String!]!]!` |
| `nonNull(list('String'))` | `[String]!` | `[String!]!` |
| `nonNull(list(nonNull('String')))` | `[String!]!` | `[String!]!` |
| `nonNull(nonNull('String'))` | `Error` | `Error` |
| `nonNull(nullable('String'))` | `Error` | `Error` |


## nonNull

Expand Down Expand Up @@ -69,21 +66,20 @@ In case `nonNullDefaults.output` or `nonNullDefaults.input` is `true`, `nonNull`

Below are some more usage examples of `nonNull`:

| Example | Produced GraphQL Type | nonNullDefaults |
| ---------------------------------- | --------------------- | ----------------- |
| `nonNull('String')` | `String!` | `false` |
| `nonNull(list('String'))` | `[String]!` | `false` |
| `list(nonNull('String'))` | `[String!]` | `false` |
| `nonNull(list(nonNull('String')))` | `[String!]!` | `false` |
| `nonNull(nonNull('String'))` | `Error` | `true` || `false` |
| `nonNull(nullable('String'))` | `Error` | `true` || `false` |

| Example | Produced GraphQL Type (nonNullDefaults = true) | Produced GraphQL Type (nonNullDefaults = false) |
| ---------------------------------- | ---------------------------------------------- | ----------------------------------------------- |
| `nonNull('String')` | `String!` | `String!` |
| `nonNull(list('String'))` | `[String]!` | `[String]!` |
| `list(nonNull('String'))` | `[String!]` | `[String!]` |
| `nonNull(list(nonNull('String')))` | `[String!]!` | `[String!]!` |
| `nonNull(nonNull('String'))` | `String!` | `String!` |
| `nonNull(nullable('String'))` | `String! | `String! |

## nullable

The `nullable()` helper is intended to be used when `nonNullDefaults.output` or `nonNullDefaults.input` is `true`.

While input and output types are nullable by default, you can set them to be non-nullable by default thanks to the `nonNullDefaults` configuration.
While input and output types are nullable by default, you can set them to be non-nullable globally, or per-type, thanks to the `nonNullDefaults` configuration.

When types are non-nullable by default:

Expand Down Expand Up @@ -124,5 +120,6 @@ Below are some more usage examples of `nullable()`:
| `nullable(list('String'))` | `[String]` | `[String!]` |
| `list(nullable('String'))` | `[String]` | `[String]!` |
| `nullable(list(nullable('String')))` | `[String]` | `[String]` |
| `nullable(nonNull('String'))` | `Error` | `Error` |
| `nullable(nullable('String'))` | `Error` | `Error` |
| `nullable(nonNull('String'))` | `String` | `String` |
| `nonNull(nullable('String'))` | `String!` | `String!` |
| `nullable(nullable('String'))` | `String` | `String` |
4 changes: 2 additions & 2 deletions docs/content/015-api/090-mutation-field.mdx
Expand Up @@ -32,10 +32,10 @@ export const createUser = extendType({
})
```

You can also use it with a function as the first argument, which will pass the `t` provided to the definition block, especially useful when using with the [connection plugin](/pluginss/connection):
You can also use it with a function as the first argument, which will pass the `t` provided to the definition block, especially useful when using with the [connection plugin](/plugins/connection):

```ts
export const usersQueryField = mutationField(t => {
export const usersQueryField = mutationField((t) => {
t.connectionField('updateUsersList', {
type: SomeType,
resolve() {
Expand Down
4 changes: 2 additions & 2 deletions docs/content/015-api/100-query-field.mdx
Expand Up @@ -36,10 +36,10 @@ export const createUser = extendType({
})
```

You can also use it with a function as the first argument, which will pass the `t` provided to the definition block, especially useful when using with the [connection plugin](/pluginss/connection):
You can also use it with a function as the first argument, which will pass the `t` provided to the definition block, especially useful when using with the [connection plugin](/plugins/connection):

```ts
export const usersQueryField = queryField(t => {
export const usersQueryField = queryField((t) => {
t.connectionField('users', {
type: SomeType,
resolve() {
Expand Down
25 changes: 20 additions & 5 deletions docs/content/015-api/110-plugins.mdx
Expand Up @@ -14,10 +14,10 @@ Nexus ships with a plugin API which allows you to define your own abstractions w

We also ship with several plugins out of the box, and will have more in the near future:

- [Connection Plugin](/pluginss/connection)
- [Field Authorize Plugin](/pluginss/field-authorize)
- [Nullability Guard Plugin](/pluginss/nullability-guard)
- [Query Complexity Plugin](/pluginss/query-complexity)
- [Connection Plugin](/plugins/connection)
- [Field Authorize Plugin](/plugins/field-authorize)
- [Nullability Guard Plugin](/plugins/nullability-guard)
- [Query Complexity Plugin](/plugins/query-complexity)

### Defining a sample plugin:

Expand Down Expand Up @@ -69,7 +69,7 @@ plugin({
### onAfterBuild(schema)

The "onAfterBuild" hook is provided the schema, and is useful to validate contract of the schema
with the expectations of the plugin. The [nullabilityGuard](/pluginss/nullability-guard)
with the expectations of the plugin. The [nullabilityGuard](/plugins/nullability-guard)

```ts
plugin({
Expand Down Expand Up @@ -146,6 +146,21 @@ const LogMutationTimePlugin = plugin({
})
```

### onAddOutputField

Called when the `.addField` is called internally in the builder, before constructing the field.
See the "declarativeWrapping" plugin for an example use.

### onAddInputField

Called when the `.addField` is called internally in the builder, before constructing the field.
See the "declarativeWrapping" plugin for an example use.

### onAddArg

Called just before a Nexus arg is constructed into an GraphQLArgumentConfig.
See the "declarativeWrapping" plugin for an example use.

### onMissingType

The "onMissingType" hook occurs when a type is provided as a string, but was never explicitly defined.
Expand Down
@@ -1,5 +1,5 @@
---
title: Connection
title: Relay Connection
---

## Connection Plugin
Expand Down Expand Up @@ -47,7 +47,7 @@ You can use this with helpers provided via [graphql-relay-js](https://github.com
```ts
import { connectionFromArray } from 'graphql-relay'

export const usersQueryField = queryField(t => {
export const usersQueryField = queryField((t) => {
t.connectionField('users', {
type: User,
async resolve(root, args, ctx, info) {
Expand Down Expand Up @@ -122,7 +122,7 @@ query IncludeNodesFieldExample {
The `queryField` or `mutationField` helpers may accept a function rather than a field name, which will be shorthand for the query builder:

```ts
export const usersField = queryField(t => {
export const usersField = queryField((t) => {
t.connectionField('users', {
type: Users,
nodes(root, args, ctx, info) {
Expand Down
51 changes: 51 additions & 0 deletions docs/content/030-plugins/02-declarativeWrapping.mdx
@@ -0,0 +1,51 @@
---
title: Declarative Wrapping (null/list)
---

## Declarative Wrapping

The declarative wrapping plugin adds the pre 0.19 API of `list: true | boolean[]`, `nullable: boolean`, and `required: boolean`
on field & argument definitions.

To install, add the `declarativeWrapping` to the `makeSchema.plugins` array, along with any other plugins
you'd like to include:

```ts
import { makeSchema, declarativeWrapping } from '@nexus/schema'

const schema = makeSchema({
// ... types, etc,
plugins: [
// ... other plugins
declarativeWrapping(),
],
})
```

You can now use the object style APIs on any args & field definitions.

```ts
export const User = objectType({
name: 'User',
definition(t) {
t.string('someNullField', {
nullable: true,
})
t.string('someRequiredField', {
nullable: false,
resolve: () => 'Some Field',
})
t.string('someList', {
list: true,
resolve: () => [],
})
t.string('someListOfLists', {
list: [true, true],
args: {
int: intArg({ required: true }),
},
resolve: () => [],
})
},
})
```
File renamed without changes.
4 changes: 2 additions & 2 deletions docs/content/040-adoption-guides/010-prisma-users.mdx
Expand Up @@ -23,10 +23,10 @@ Vanilla works, but there's something better. The Nexus Prisma _plugin_ (`nexus-p
- Declarative APIs for projecting types from your Prisma schema onto your GraphQL Schema
- Declarative APIs for creating mutations and queries (including automatically implemented resolvers!)

For more info checkout the [Prisma plugin documentation](/pluginss/prisma).
For more info checkout the [Prisma plugin documentation](/plugins/prisma).

## Learning Path

1. Read [Welcome to Nexus](/)
2. Do [The Nexus Tutorial](/getting-started/tutorial/chapter-introduction)
3. If using the Prisma plugin (you should!) read [Welcome to Nexus Prisma](/pluginss/prisma)
3. If using the Prisma plugin (you should!) read [Welcome to Nexus Prisma](/plugins/prisma)
3 changes: 1 addition & 2 deletions examples/kitchen-sink/src/kitchen-sink-definitions.ts
Expand Up @@ -214,9 +214,8 @@ export const Query = objectType({
},
})

t.connectionField('deprecatedConnection', {
t.nonNull.connectionField('deprecatedConnection', {
type: 'Boolean',
nullable: false,
deprecation: 'Dont use this, use booleanConnection instead',
nodes() {
return [true]
Expand Down

0 comments on commit 306dbaa

Please sign in to comment.