Skip to content

Commit 73b4d4d

Browse files
committed
[RFC] Expand GraphQL language
This proposes merging the schema language into the GraphQL language. After some time of thinking about this, I believe this is the best path. There is only one "GraphQL language" and thus only one spec for a parser. I believe this will have substantial simplification forces on GraphQL server libs and client tools and the APIs they expose. This also means the validation phase can be designed to accept both "client GraphQL" and "server GraphQL" rather than two separate utilities with two separate AST and rule structures. Instead we're just likely to want to introduce standard rule sets for each environment. The drawback is explaining the semantics. This diff primarily adds type definition syntax to GraphQL, and it should be made clear that *at present* GraphQL servers do not have semantics for these and in fact will not execute queries which contain them.
1 parent 9afdada commit 73b4d4d

File tree

21 files changed

+851
-899
lines changed

21 files changed

+851
-899
lines changed

src/execution/__tests__/executor.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,4 +647,32 @@ describe('Execute: Handles basic execution tasks', () => {
647647
]);
648648
});
649649

650+
it('fails to execute a query containing a type definition', async () => {
651+
var query = parse(`
652+
{ foo }
653+
654+
type Query { foo: String }
655+
`);
656+
657+
var schema = new GraphQLSchema({
658+
query: new GraphQLObjectType({
659+
name: 'Query',
660+
fields: {
661+
foo: { type: GraphQLString }
662+
}
663+
})
664+
});
665+
666+
var caughtError;
667+
try {
668+
await execute(schema, query);
669+
} catch (error) {
670+
caughtError = error;
671+
}
672+
673+
expect(caughtError).to.deep.equal({
674+
message: 'GraphQL cannot execute a request containing a ObjectDefinition.'
675+
});
676+
});
677+
650678
});

src/execution/execute.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,10 @@ function buildExecutionContext(
174174
case Kind.FRAGMENT_DEFINITION:
175175
fragments[statement.name.value] = statement;
176176
break;
177+
default: throw new GraphQLError(
178+
`GraphQL cannot execute a request containing a ${statement.kind}.`,
179+
statement
180+
);
177181
}
178182
});
179183
if (!operationName && Object.keys(operations).length !== 1) {

src/language/schema/__tests__/parser.js renamed to src/language/__tests__/schema-parser.js

Lines changed: 40 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
import { expect } from 'chai';
1111
import { describe, it } from 'mocha';
12-
import { parseSchemaIntoAST } from '../parser';
12+
import { parse } from '../parser';
1313

1414
function createLocFn(body) {
1515
return (start, end) => ({
@@ -80,13 +80,13 @@ describe('Schema Parser', () => {
8080
type Hello {
8181
world: String
8282
}`;
83-
var doc = parseSchemaIntoAST(body);
83+
var doc = parse(body);
8484
var loc = createLocFn(body);
8585
var expected = {
86-
kind: 'SchemaDocument',
86+
kind: 'Document',
8787
definitions: [
8888
{
89-
kind: 'TypeDefinition',
89+
kind: 'ObjectDefinition',
9090
name: nameNode('Hello', loc(6, 11)),
9191
interfaces: [],
9292
fields: [
@@ -110,12 +110,12 @@ type Hello {
110110
world: String!
111111
}`;
112112
var loc = createLocFn(body);
113-
var doc = parseSchemaIntoAST(body);
113+
var doc = parse(body);
114114
var expected = {
115-
kind: 'SchemaDocument',
115+
kind: 'Document',
116116
definitions: [
117117
{
118-
kind: 'TypeDefinition',
118+
kind: 'ObjectDefinition',
119119
name: nameNode('Hello', loc(6, 11)),
120120
interfaces: [],
121121
fields: [
@@ -141,12 +141,12 @@ type Hello {
141141
it('Simple type inheriting interface', () => {
142142
var body = `type Hello implements World { }`;
143143
var loc = createLocFn(body);
144-
var doc = parseSchemaIntoAST(body);
144+
var doc = parse(body);
145145
var expected = {
146-
kind: 'SchemaDocument',
146+
kind: 'Document',
147147
definitions: [
148148
{
149-
kind: 'TypeDefinition',
149+
kind: 'ObjectDefinition',
150150
name: nameNode('Hello', loc(5, 10)),
151151
interfaces: [ typeNode('World', loc(22, 27)) ],
152152
fields: [],
@@ -161,12 +161,12 @@ type Hello {
161161
it('Simple type inheriting multiple interfaces', () => {
162162
var body = `type Hello implements Wo, rld { }`;
163163
var loc = createLocFn(body);
164-
var doc = parseSchemaIntoAST(body);
164+
var doc = parse(body);
165165
var expected = {
166-
kind: 'SchemaDocument',
166+
kind: 'Document',
167167
definitions: [
168168
{
169-
kind: 'TypeDefinition',
169+
kind: 'ObjectDefinition',
170170
name: nameNode('Hello', loc(5, 10)),
171171
interfaces: [
172172
typeNode('Wo', loc(22, 24)),
@@ -184,9 +184,9 @@ type Hello {
184184
it('Single value enum', () => {
185185
var body = `enum Hello { WORLD }`;
186186
var loc = createLocFn(body);
187-
var doc = parseSchemaIntoAST(body);
187+
var doc = parse(body);
188188
var expected = {
189-
kind: 'SchemaDocument',
189+
kind: 'Document',
190190
definitions: [
191191
{
192192
kind: 'EnumDefinition',
@@ -203,9 +203,9 @@ type Hello {
203203
it('Double value enum', () => {
204204
var body = `enum Hello { WO, RLD }`;
205205
var loc = createLocFn(body);
206-
var doc = parseSchemaIntoAST(body);
206+
var doc = parse(body);
207207
var expected = {
208-
kind: 'SchemaDocument',
208+
kind: 'Document',
209209
definitions: [
210210
{
211211
kind: 'EnumDefinition',
@@ -227,10 +227,10 @@ type Hello {
227227
interface Hello {
228228
world: String
229229
}`;
230-
var doc = parseSchemaIntoAST(body);
230+
var doc = parse(body);
231231
var loc = createLocFn(body);
232232
var expected = {
233-
kind: 'SchemaDocument',
233+
kind: 'Document',
234234
definitions: [
235235
{
236236
kind: 'InterfaceDefinition',
@@ -255,13 +255,13 @@ interface Hello {
255255
type Hello {
256256
world(flag: Boolean): String
257257
}`;
258-
var doc = parseSchemaIntoAST(body);
258+
var doc = parse(body);
259259
var loc = createLocFn(body);
260260
var expected = {
261-
kind: 'SchemaDocument',
261+
kind: 'Document',
262262
definitions: [
263263
{
264-
kind: 'TypeDefinition',
264+
kind: 'ObjectDefinition',
265265
name: nameNode('Hello', loc(6, 11)),
266266
interfaces: [],
267267
fields: [
@@ -292,13 +292,13 @@ type Hello {
292292
type Hello {
293293
world(flag: Boolean = true): String
294294
}`;
295-
var doc = parseSchemaIntoAST(body);
295+
var doc = parse(body);
296296
var loc = createLocFn(body);
297297
var expected = {
298-
kind: 'SchemaDocument',
298+
kind: 'Document',
299299
definitions: [
300300
{
301-
kind: 'TypeDefinition',
301+
kind: 'ObjectDefinition',
302302
name: nameNode('Hello', loc(6, 11)),
303303
interfaces: [],
304304
fields: [
@@ -333,13 +333,13 @@ type Hello {
333333
type Hello {
334334
world(things: [String]): String
335335
}`;
336-
var doc = parseSchemaIntoAST(body);
336+
var doc = parse(body);
337337
var loc = createLocFn(body);
338338
var expected = {
339-
kind: 'SchemaDocument',
339+
kind: 'Document',
340340
definitions: [
341341
{
342-
kind: 'TypeDefinition',
342+
kind: 'ObjectDefinition',
343343
name: nameNode('Hello', loc(6, 11)),
344344
interfaces: [],
345345
fields: [
@@ -374,13 +374,13 @@ type Hello {
374374
type Hello {
375375
world(argOne: Boolean, argTwo: Int): String
376376
}`;
377-
var doc = parseSchemaIntoAST(body);
377+
var doc = parse(body);
378378
var loc = createLocFn(body);
379379
var expected = {
380-
kind: 'SchemaDocument',
380+
kind: 'Document',
381381
definitions: [
382382
{
383-
kind: 'TypeDefinition',
383+
kind: 'ObjectDefinition',
384384
name: nameNode('Hello', loc(6, 11)),
385385
interfaces: [],
386386
fields: [
@@ -414,10 +414,10 @@ type Hello {
414414

415415
it('Simple union', () => {
416416
var body = `union Hello = World`;
417-
var doc = parseSchemaIntoAST(body);
417+
var doc = parse(body);
418418
var loc = createLocFn(body);
419419
var expected = {
420-
kind: 'SchemaDocument',
420+
kind: 'Document',
421421
definitions: [
422422
{
423423
kind: 'UnionDefinition',
@@ -433,10 +433,10 @@ type Hello {
433433

434434
it('Union with two types', () => {
435435
var body = `union Hello = Wo | Rld`;
436-
var doc = parseSchemaIntoAST(body);
436+
var doc = parse(body);
437437
var loc = createLocFn(body);
438438
var expected = {
439-
kind: 'SchemaDocument',
439+
kind: 'Document',
440440
definitions: [
441441
{
442442
kind: 'UnionDefinition',
@@ -455,10 +455,10 @@ type Hello {
455455

456456
it('Scalar', () => {
457457
var body = `scalar Hello`;
458-
var doc = parseSchemaIntoAST(body);
458+
var doc = parse(body);
459459
var loc = createLocFn(body);
460460
var expected = {
461-
kind: 'SchemaDocument',
461+
kind: 'Document',
462462
definitions: [
463463
{
464464
kind: 'ScalarDefinition',
@@ -476,10 +476,10 @@ type Hello {
476476
input Hello {
477477
world: String
478478
}`;
479-
var doc = parseSchemaIntoAST(body);
479+
var doc = parse(body);
480480
var loc = createLocFn(body);
481481
var expected = {
482-
kind: 'SchemaDocument',
482+
kind: 'Document',
483483
definitions: [
484484
{
485485
kind: 'InputObjectDefinition',
@@ -505,16 +505,7 @@ input Hello {
505505
input Hello {
506506
world(foo: Int): String
507507
}`;
508-
expect(() => parseSchemaIntoAST(body)).to.throw('Error');
508+
expect(() => parse(body)).to.throw('Error');
509509
});
510510

511-
it('Reject query keywords', () => {
512-
var body = `query Foo { field }`;
513-
expect(() => parseSchemaIntoAST(body)).to.throw('Error');
514-
});
515-
516-
it('Reject query shorthand', () => {
517-
var body = `{ field }`;
518-
expect(() => parseSchemaIntoAST(body)).to.throw('Error');
519-
});
520511
});

src/language/schema/__tests__/printer.js renamed to src/language/__tests__/schema-printer.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import { expect } from 'chai';
1111
import { describe, it } from 'mocha';
1212
import { readFileSync } from 'fs';
1313
import { join } from 'path';
14-
import { parseSchemaIntoAST } from '../parser';
15-
import { printSchema } from '../printer';
14+
import { parse } from '../parser';
15+
import { print } from '../printer';
1616

1717
describe('Printer', () => {
1818

@@ -21,12 +21,12 @@ describe('Printer', () => {
2121
kind: 'ScalarDefinition',
2222
name: { kind: 'Name', value: 'foo' }
2323
};
24-
expect(printSchema(ast)).to.equal('scalar foo');
24+
expect(print(ast)).to.equal('scalar foo');
2525
});
2626

2727
it('produces helpful error messages', () => {
2828
var badAst1 = { random: 'Data' };
29-
expect(() => printSchema(badAst1)).to.throw(
29+
expect(() => print(badAst1)).to.throw(
3030
'Invalid AST Node: {"random":"Data"}'
3131
);
3232
});
@@ -37,17 +37,17 @@ describe('Printer', () => {
3737
);
3838

3939
it('does not alter ast', () => {
40-
var ast = parseSchemaIntoAST(kitchenSink);
40+
var ast = parse(kitchenSink);
4141
var astCopy = JSON.parse(JSON.stringify(ast));
42-
printSchema(ast);
42+
print(ast);
4343
expect(ast).to.deep.equal(astCopy);
4444
});
4545

4646
it('prints kitchen sink', () => {
4747

48-
var ast = parseSchemaIntoAST(kitchenSink);
48+
var ast = parse(kitchenSink);
4949

50-
var printed = printSchema(ast);
50+
var printed = print(ast);
5151

5252
expect(printed).to.equal(
5353
`type Foo implements Bar {

0 commit comments

Comments
 (0)