Skip to content

Commit

Permalink
Implement nullability operators in query language
Browse files Browse the repository at this point in the history
The ["Nullability RFC" for GraphQL](graphql/graphql-wg#694)
allows fields to individually be marked as optional or required in a query by the
client-side. ([See Strawman Proposal](graphql/graphql-spec#867))

If a field is marked as optional then it's allowed to be missing and `null`, which
can control where missing values cascade to:

```graphql
query {
  me {
    name?
  }
}
```

If a field is marked as required it may never be allowed to become `null` and must
cascade if it otherwise would have been set to `null`:

```graphql
query {
  me {
    name!
  }
}
```
  • Loading branch information
kitten committed Oct 8, 2021
1 parent 1a5ff4f commit fb629e1
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 3 deletions.
8 changes: 8 additions & 0 deletions alias/language/__tests__/printer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ describe('Printer: Query document', () => {
name
}
`);

const queryWithNullabilityFields = parse('query { id?, name! }');
expect(print(queryWithNullabilityFields)).toBe(dedent`
{
id?
name!
}
`);
});

it('prints query with variable directives', () => {
Expand Down
9 changes: 9 additions & 0 deletions alias/language/parser.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,20 @@ const directives = match()`
${directive}*
`;

const nullability = match(null, (x) => {
return x[0] === '?' ? 'optional' : 'required';
})`
:${ignored}?
${/[?!]/}
`;

const field = match(Kind.FIELD, (x) => {
let i = 0;
return {
kind: x.tag,
alias: x[1].kind === Kind.NAME ? x[i++] : undefined,
name: x[i++],
required: typeof x[i] === 'string' ? x[i++] : 'unset',
arguments: x[i++],
directives: x[i++],
selectionSet: x[i++],
Expand All @@ -164,6 +172,7 @@ const field = match(Kind.FIELD, (x) => {
(?: ${ignored}? ${':'} ${ignored}?)
${name}
)?
${nullability}?
${args}
${directives}
${() => selectionSet}?
Expand Down
10 changes: 7 additions & 3 deletions alias/language/printer.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,15 @@ export function print(node) {
);

case 'Field':
let prefix = wrap('', print(node.alias), ': ') + print(node.name);
if (node.required === 'optional') {
prefix += '?';
} else if (node.required === 'required') {
prefix += '!';
}
return join(
[
wrap('', print(node.alias), ': ') +
print(node.name) +
wrap('(', join(print(node.arguments), ', '), ')'),
prefix + wrap('(', join(print(node.arguments), ', '), ')'),
join(print(node.directives), ' '),
print(node.selectionSet),
],
Expand Down

0 comments on commit fb629e1

Please sign in to comment.