UNPKG

13.2 kBMarkdownView Raw
1# eslint-plugin-graphql
2[![npm version](https://badge.fury.io/js/eslint-plugin-graphql.svg)](https://badge.fury.io/js/eslint-plugin-graphql)
3[![Build Status](https://travis-ci.org/apollographql/eslint-plugin-graphql.svg?branch=master)](https://travis-ci.org/apollographql/eslint-plugin-graphql)
4[![Get on Slack](https://img.shields.io/badge/slack-join-orange.svg)](http://www.apollostack.com/#slack)
5
6An ESLint plugin that checks tagged query strings inside JavaScript, or queries inside `.graphql` files, against a GraphQL schema.
7
8```
9npm install eslint-plugin-graphql
10```
11
12![Screenshot from Atom](https://github.com/apollostack/eslint-plugin-graphql/raw/master/screenshot.png)
13
14`eslint-plugin-graphql` has built-in settings for three GraphQL clients out of the box:
15
161. [Apollo client](http://docs.apollostack.com/apollo-client/index.html)
172. [Relay](https://facebook.github.io/relay/)
183. [Lokka](https://github.com/kadirahq/lokka)
19
20If you want to lint your GraphQL schema, rather than queries, check out [cjoudrey/graphql-schema-linter](https://github.com/cjoudrey/graphql-schema-linter).
21
22### Importing schema JSON
23
24You'll need to import your [introspection query result](https://github.com/graphql/graphql-js/blob/master/src/utilities/introspectionQuery.js) or the schema as a string in the Schema Language format. This can be done if you define your ESLint config in a JS file. Note: we're always looking for better ways to get the schema, so please open an issue with suggestions.
25
26### Common options
27
28All of the rules provided by this plugin have a few options in common. There are examples of how to use these with Apollo, Relay, Lokka and literal files further down.
29
30- `env`: Import default settings for your GraphQL client. Supported values: `'apollo'`, `'relay'`, `'lokka'`, `'literal'`. Defaults to `'apollo'`. This is used for the slight parsing differences in the GraphQL syntax between Apollo, Relay and Lokka, as well as giving nice defaults to some other options.
31
32- `tagName`: The name of the template literal tag that this plugin should look for when searching for GraphQL queries. It has different defaults depending on the `env` option:
33
34 - `'relay'`: `'Relay.QL'`
35 - `'internal'`: Special automatic value
36 - others: `'gql'`
37
38You also have to specify a schema. You can either do it using _one_ of these options:
39
40- `schemaJson`: Your schema as JSON.
41- `schemaJsonFilepath`: The absolute path to your schema as a .json file.
42- `schemaString`: Your schema in the Schema Language format as a string.
43
44Alternatively, you can use a [.graphqlconfig](https://github.com/graphcool/graphql-config) file instead of the above three options. If you do there's one more option to know about:
45
46- `projectName`: In case you specify multiple schemas in your `.graphqlconfig` file, choose which one to use by providing the project name here as a string.
47
48There's an example on how to use a `.graphqlconfig` file further down.
49
50### Identity template literal tag
51
52This plugin relies on GraphQL queries being prefixed with a special tag. In Relay and Apollo, this is always done, but other clients often take query strings without a tag. In this case, you can define an identity tag that doesn't do anything except for tell the linter this is a GraphQL query:
53
54```js
55global.gql = (literals, ...substitutions) => {
56 let result = "";
57
58 // run the loop only for the substitution count
59 for (let i = 0; i < substitutions.length; i++) {
60 result += literals[i];
61 result += substitutions[i];
62 }
63
64 // add the last literal
65 result += literals[literals.length - 1];
66
67 return result;
68}
69```
70
71Code snippet taken from: <https://leanpub.com/understandinges6/read#leanpub-auto-multiline-strings>
72
73Note: The linter rule could be extended to identify calls to various specific APIs to eliminate the need for a template literal tag, but this might just make the implementation a lot more complex for little benefit.
74
75### GraphQL literal files
76
77This plugin also lints GraphQL literal files ending on `.gql` or `.graphql`.
78In order to do so set `env` to `'literal'` in your `.eslintrc.js` and tell eslint to check these files as well.
79
80```bash
81eslint . --ext .js --ext .gql --ext .graphql
82```
83
84### Example config for Apollo
85
86```js
87// In a file called .eslintrc.js
88module.exports = {
89 parser: "babel-eslint",
90 rules: {
91 "graphql/template-strings": ['error', {
92 // Import default settings for your GraphQL client. Supported values:
93 // 'apollo', 'relay', 'lokka', 'literal'
94 env: 'apollo',
95
96 // Import your schema JSON here
97 schemaJson: require('./schema.json'),
98
99 // OR provide absolute path to your schema JSON
100 // schemaJsonFilepath: path.resolve(__dirname, './schema.json'),
101
102 // OR provide the schema in the Schema Language format
103 // schemaString: printSchema(schema),
104
105 // tagName is gql by default
106 }]
107 },
108 plugins: [
109 'graphql'
110 ]
111}
112```
113
114### Example config for Relay
115
116```js
117// In a file called .eslintrc.js
118module.exports = {
119 parser: "babel-eslint",
120 rules: {
121 "graphql/template-strings": ['error', {
122 // Import default settings for your GraphQL client. Supported values:
123 // 'apollo', 'relay', 'lokka', 'literal'
124 env: 'relay',
125
126 // Import your schema JSON here
127 schemaJson: require('./schema.json'),
128
129 // OR provide absolute path to your schema JSON
130 // schemaJsonFilepath: path.resolve(__dirname, './schema.json'),
131
132 // OR provide the schema in the Schema Language format
133 // schemaString: printSchema(schema),
134
135 // tagName is set for you to Relay.QL
136 }]
137 },
138 plugins: [
139 'graphql'
140 ]
141}
142```
143
144### Example config for Lokka
145
146```js
147// In a file called .eslintrc.js
148module.exports = {
149 parser: "babel-eslint",
150 rules: {
151 "graphql/template-strings": ['error', {
152 // Import default settings for your GraphQL client. Supported values:
153 // 'apollo', 'relay', 'lokka', 'literal'
154 env: 'lokka',
155
156 // Import your schema JSON here
157 schemaJson: require('./schema.json'),
158
159 // OR provide absolute path to your schema JSON
160 // schemaJsonFilepath: path.resolve(__dirname, './schema.json'),
161
162 // OR provide the schema in the Schema Language format
163 // schemaString: printSchema(schema),
164
165 // Optional, the name of the template tag, defaults to 'gql'
166 tagName: 'gql'
167 }]
168 },
169 plugins: [
170 'graphql'
171 ]
172}
173```
174
175### Example config for literal graphql files
176
177```js
178// In a file called .eslintrc.js
179module.exports = {
180 parser: "babel-eslint",
181 rules: {
182 "graphql/template-strings": ['error', {
183 // Import default settings for your GraphQL client. Supported values:
184 // 'apollo', 'relay', 'lokka', 'literal'
185 env: 'literal',
186
187 // Import your schema JSON here
188 schemaJson: require('./schema.json'),
189
190 // OR provide absolute path to your schema JSON
191 // schemaJsonFilepath: path.resolve(__dirname, './schema.json'),
192
193 // OR provide the schema in the Schema Language format
194 // schemaString: printSchema(schema),
195
196 // tagName is set automatically
197 }]
198 },
199 plugins: [
200 'graphql'
201 ]
202}
203```
204
205### Additional Schemas or Tags
206
207This plugin can be used to validate against multiple schemas by identifying them with different tags. This is useful for applications interacting with multiple GraphQL systems. Additional schemas can simply be appended to the options list:
208
209```js
210module.exports = {
211 parser: "babel-eslint",
212 rules: {
213 "graphql/template-strings": ['error', {
214 env: 'apollo',
215 tagName: 'FirstGQL',
216 schemaJson: require('./schema-first.json')
217 }, {
218 env: 'relay',
219 tagName: 'SecondGQL',
220 schemaJson: require('./schema-second.json')
221 }]
222 },
223 plugins: [
224 'graphql'
225 ]
226}
227```
228
229### Example config when using [.graphqlconfig](https://github.com/graphcool/graphql-config)
230
231If you have `.graphqlconfig` file in the root of your repo you can omit schema-related
232properties (`schemaJson`, `schemaJsonFilepath` and `schemaString`) from rule config.
233
234```js
235// In a file called .eslintrc.js
236module.exports = {
237 parser: "babel-eslint",
238 rules: {
239 "graphql/template-strings": ['error', {
240 // Import default settings for your GraphQL client. Supported values:
241 // 'apollo', 'relay', 'lokka', 'literal'
242 env: 'literal'
243 // no need to specify schema here, it will be automatically determined using .graphqlconfig
244 }]
245 },
246 plugins: [
247 'graphql'
248 ]
249}
250```
251
252In case you use additional schemas, specify `projectName` from `.graphqlconfig` for each `tagName`:
253```js
254module.exports = {
255 parser: "babel-eslint",
256 rules: {
257 "graphql/template-strings": ['error', {
258 env: 'apollo',
259 tagName: 'FirstGQL',
260 projectName: 'FirstGQLProject'
261 }, {
262 env: 'relay',
263 tagName: 'SecondGQL',
264 projectName: 'SecondGQLProject'
265 }]
266 },
267 plugins: [
268 'graphql'
269 ]
270}
271```
272
273### Selecting Validation Rules
274
275GraphQL validation rules can be configured in the eslint rule configuration using the `validators` option. The default selection depends on the `env` setting. If no `env` is specified, all rules are enabled by default.
276
277The `validators` setting can be set either to a list of specific validator names or to the special value `"all"`.
278
279```js
280module.exports = {
281 parser: "babel-eslint",
282 rules: {
283 "graphql/template-strings": ['error', {
284 env: 'apollo',
285 validators: 'all',
286 tagName: 'FirstGQL',
287 schemaJson: require('./schema-first.json')
288 }, {
289 validators: ['FieldsOnCorrectType'],
290 tagName: 'SecondGQL',
291 schemaJson: require('./schema-second.json')
292 }]
293 },
294 plugins: [
295 'graphql'
296 ]
297}
298```
299
300The full list of available validators is:
301 - `ArgumentsOfCorrectType`
302 - `DefaultValuesOfCorrectType`
303 - `FieldsOnCorrectType`
304 - `FragmentsOnCompositeTypes`
305 - `KnownArgumentNames`
306 - `KnownDirectives` (*disabled by default in `relay`*)
307 - `KnownFragmentNames` (*disabled by default in all envs*)
308 - `KnownTypeNames`
309 - `LoneAnonymousOperation`
310 - `NoFragmentCycles`
311 - `NoUndefinedVariables` (*disabled by default in `relay`*)
312 - `NoUnusedFragments` (*disabled by default in all envs*)
313 - `NoUnusedVariables`
314 - `OverlappingFieldsCanBeMerged`
315 - `PossibleFragmentSpreads`
316 - `ProvidedNonNullArguments` (*disabled by default in `relay`*)
317 - `ScalarLeafs` (*disabled by default in `relay`*)
318 - `UniqueArgumentNames`
319 - `UniqueFragmentNames`
320 - `UniqueInputFieldNames`
321 - `UniqueOperationNames`
322 - `UniqueVariableNames`
323 - `VariablesAreInputTypes`
324 - `VariablesInAllowedPosition`
325
326### Named Operations Validation Rule
327
328The Named Operation rule validates that all operations are named. Naming operations is valuable for including in server-side logs and debugging.
329
330**Pass**
331```
332query FetchUsername {
333 viewer {
334 name
335 }
336}
337```
338
339**Fail**
340```
341query {
342 viewer {
343 name
344 }
345}
346```
347
348The rule is defined as `graphql/named-operations`.
349
350```js
351// In a file called .eslintrc.js
352module.exports = {
353 parser: "babel-eslint",
354 rules: {
355 "graphql/template-strings": ['error', {
356 env: 'apollo',
357 schemaJson: require('./schema.json'),
358 }],
359 "graphql/named-operations": ['warn', {
360 schemaJson: require('./schema.json'),
361 }],
362 },
363 plugins: [
364 'graphql'
365 ]
366}
367```
368### Required Fields Validation Rule
369
370The Required Fields rule validates that any specified required field is part of the query, but only if that field is available in schema. This is useful to ensure that query results are cached properly in the client.
371
372**Pass**
373```
374// 'uuid' required and present in the schema
375
376schema {
377 query {
378 viewer {
379 name
380 uuid
381 }
382 }
383}
384
385query ViewerName {
386 viewer {
387 name
388 uuid
389 }
390}
391```
392
393**Pass**
394```
395// 'uuid' usually required but not present in the schema here
396
397schema {
398 query {
399 viewer {
400 name
401 }
402 }
403}
404
405query ViewerName {
406 viewer {
407 name
408 }
409}
410```
411
412**Fail**
413```
414// 'uuid' required and present in the schema
415
416schema {
417 query {
418 viewer {
419 uuid
420 name
421 }
422 }
423}
424
425query ViewerName {
426 viewer {
427 name
428 }
429}
430```
431
432The rule is defined as `graphql/required-fields` and requires the `requiredFields` option.
433
434```js
435// In a file called .eslintrc.js
436module.exports = {
437 rules: {
438 'graphql/required-fields': [
439 'error',
440 {
441 env: 'apollo',
442 schemaJsonFilepath: path.resolve(__dirname, './schema.json'),
443 requiredFields: ['uuid'],
444 },
445 ],
446 },
447 plugins: [
448 'graphql'
449 ]
450}
451```
452
453### Capitalization of a first letter of a Type name
454
455This rule enforces that first letter of types is capitalized
456
457**Pass**
458```
459query {
460 someUnion {
461 ... on SomeType {
462 someField
463 }
464 }
465}
466```
467
468**Fail**
469```
470query {
471 someUnion {
472 ... on someType {
473 someField
474 }
475 }
476}
477```
478
479The rule is defined as `graphql/capitalized-type-name`.
480
481```js
482// In a file called .eslintrc.js
483module.exports = {
484 parser: "babel-eslint",
485 rules: {
486 "graphql/template-strings": ['error', {
487 env: 'apollo',
488 schemaJson: require('./schema.json'),
489 }],
490 "graphql/capitalized-type-name": ['warn', {
491 schemaJson: require('./schema.json'),
492 }],
493 },
494 plugins: [
495 'graphql'
496 ]
497}
498```