1 | # graphql-toolkit
|
2 |
|
3 | A set of utils for faster development of GraphQL tools. Use these utils if you are creating a tool that loads schema/documents, merges schemas, scan for schema/documents/resolvers files.
|
4 |
|
5 | Included tools:
|
6 |
|
7 | ### Schema Loading
|
8 |
|
9 | These utils are useful for scanning, loading and building a GraphQL schema from any input.
|
10 |
|
11 | You can either specify a GraphQL endpoint, local introspection JSON file, code file that `export`s a GraphQLSchema, AST string and `.graphql` files (with support for `glob` expression).
|
12 |
|
13 | It also merges all found schema files into a complete schema, and has support for `#import` syntax (using [`graphql-import`](https://github.com/prisma/graphql-import)).
|
14 |
|
15 | You can also extend the loads by implementing you own loader (implement the interface `SchemaLoader`).
|
16 |
|
17 | The schema loading util is using loaders, and implemented using [chain-of-responsibility pattern](https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern).
|
18 |
|
19 | You don't have to specify which loader to use - just provide your input and this utils will detect it automatically.
|
20 |
|
21 | Usage:
|
22 |
|
23 | ```ts
|
24 | import { loadSchema } from 'graphql-toolkit';
|
25 |
|
26 | const schema1 = loadSchema('type A { foo: String }'); // load from string
|
27 | const schema2 = loadSchema('http://localhost:3000/graphql'); // load from endpoint
|
28 | const schema3 = loadSchema('./schema.json'); // load from local json file
|
29 | const schema4 = loadSchema('schema.graphql'); // load from a single schema file
|
30 | const schema5 = loadSchema('./src/**/*.graphql'); // load from multiple files using glob
|
31 | ```
|
32 |
|
33 | ### Documents Loading
|
34 |
|
35 | Similar to schema loading - but meant to use for GraphQL documents (query/mutation/subscription/fragment).
|
36 |
|
37 | You an specify any input as source, and this utils will detect it automatically.
|
38 |
|
39 | It also extracts usages of `gql` from code files using [`graphql-tag-pluck`](https://github.com/DAB0mB/graphql-tag-pluck).
|
40 |
|
41 | Usage:
|
42 |
|
43 | ```ts
|
44 | import { loadDocuments } from 'graphql-toolkit';
|
45 |
|
46 | const document1 = loadDocuments('query { f }'); // load from string
|
47 | const document2 = loadDocuments('./users.query.graphql'); // load from a single file
|
48 | const document3 = loadDocuments('./src/**/*.graphql'); // load from multiple files using glob
|
49 | const document4 = loadDocuments('./src/my-component.ts'); // load from code file
|
50 | ```
|
51 |
|
52 | ### Epoxy
|
53 |
|
54 | Originally implemented in [graphql-modules](https://github.com/Urigo/graphql-modules). This tools merged GraphQL type definitions and schema. It aims to merge all possible types, interfaces, enums and unions, without conflicts.
|
55 |
|
56 | It also merged resolvers by using deep-merge, so you can separate your resolvers implementating across multiple objects and the merge it into a single `resolvers` object.
|
57 |
|
58 | ```graphql
|
59 | # a1.graphql
|
60 | type A {
|
61 | f1: String
|
62 | }
|
63 |
|
64 | # a2.graphql
|
65 | type A {
|
66 | f2: String
|
67 | }
|
68 | ```
|
69 |
|
70 | Will result:
|
71 | ```graphql
|
72 | type A {
|
73 | f1: String
|
74 | f2: String
|
75 | }
|
76 | ```
|
77 |
|
78 | ### Sonar
|
79 |
|
80 | Sonar is a small util that scans you file-system and find GraphQL files (`.graphql`) and resolvers files (`.js`/`.ts`) and loads them (using `readFile` for GraphQL files and `require` for resolvers files).
|
81 |
|
82 | ### Other Utils
|
83 |
|
84 | There are some more utils under `utils` directory:
|
85 |
|
86 | #### `validateGraphQlDocuments`
|
87 |
|
88 | A tool for validating GraphQL documents (query/mutation/subscription/fragment) against the schema.
|
89 |
|
90 | It uses the original validation logic from `graphql` package, but suspressing some validation rules, and focuses on validating the fields and the structure of the documents.
|
91 |
|
92 | #### `extractDocumentStringFromCodeFile`
|
93 |
|
94 | This method gets a source code file, and using `graphql-tag-pluck` tries to extract GraphQL AST from it.
|
95 |
|
96 | #### `getDirectives`
|
97 |
|
98 | This method accepts `GraphQLSchema` and any AST node, and tries to extract the GraphQL directives of the AST node.
|
99 |
|
100 | It returnes a `Map` of the directive name and the arguments of it.
|
101 |
|
102 | #### `getFieldsWithDirectives`
|
103 |
|
104 | This method accepts a GraphQL `DocumentNode` of a GraphQL types definition and creates a map between GraphQL `type.field` and the directives it has.
|
105 |
|
106 | It's useful for getting an easy-to-use structure of the directives that are decorating the schema.
|
107 |
|
108 | #### `getImplementingTypes`
|
109 |
|
110 | This method accepts `GraphQLSchema` object and a name of a GraphQL interface, and returns an array of all the GraphQL types that are implementing the GraphQL `interface`.
|
111 |
|
112 | #### `getSchemaDirectiveFromDirectiveResolver`
|
113 |
|
114 | This method accepts a name of a GraphQL Directive and its resolver function; using this method you can easily generate `SchemaDirective` with a single resolver function.
|
115 |
|
116 | #### `composeResolvers`
|
117 |
|
118 | This method accepts `IResolvers` object and mappings for composition functions that would be run before resolver itself.
|
119 |
|
120 | Instead of doing this,
|
121 |
|
122 | ```js
|
123 | const resolvers ={
|
124 | Query: {
|
125 | myQuery: (root, args, context) => {
|
126 | // Make sure that the user is authenticated
|
127 | if (!context.currentUser) {
|
128 | throw new Error('You are not authenticated!');
|
129 | }
|
130 |
|
131 | // Make sure that the user has the correct roles
|
132 | if (!context.currentUser.roles || context.currentUser.roles.includes('EDITOR')) {
|
133 | throw new Error('You are not authorized!');
|
134 | }
|
135 |
|
136 | // Business logic
|
137 | if (args.something === '1') {
|
138 | return true;
|
139 | }
|
140 |
|
141 | return false;
|
142 | },
|
143 | },
|
144 | };
|
145 | ```
|
146 |
|
147 | You can do;
|
148 |
|
149 | ```js
|
150 | const resolvers ={
|
151 | Query: {
|
152 | myQuery: (root, args, context) => {
|
153 | if (args.something === '1') {
|
154 | return true;
|
155 | }
|
156 |
|
157 | return false;
|
158 | },
|
159 | },
|
160 | };
|
161 |
|
162 | const isAuthenticated = () => next => async (root, args, context, info) => {
|
163 | if (!context.currentUser) {
|
164 | throw new Error('You are not authenticated!');
|
165 | }
|
166 |
|
167 | return next(root, args, context, info);
|
168 | };
|
169 |
|
170 | const hasRole = (role: string) => next => async (root, args, context, info) => {
|
171 | if (!context.currentUser.roles || context.currentUser.roles.includes(role)) {
|
172 | throw new Error('You are not authorized!');
|
173 | }
|
174 |
|
175 | return next(root, args, context, info);
|
176 | };
|
177 |
|
178 | const resolversComposition = {
|
179 | 'Query.myQuery': [isAuthenticated(), hasRole('EDITOR')],
|
180 | };
|
181 |
|
182 | const composedResolvers = composeResolvers(resolvers, resolversComposition);
|
183 | ```
|
184 |
|
185 | #### `extractResolversFromSchema`
|
186 |
|
187 | This methods accepts `GraphQLSchema` object, and returns a map with field resolver functions of all types inside the schema as in [`IResolvers` interface of `graphql-tools`.](https://www.apollographql.com/docs/graphql-tools/resolvers.html)
|
188 |
|
189 | #### `extractFieldResolversFromObjectType`
|
190 |
|
191 | This methods accepts `GraphQLObjectType` or `GraphQLInterfaceType` object, and returns a map with field resolvers of given type.
|