UNPKG

11.2 kBMarkdownView Raw
1# `graphql-typescript-definitions`
2
3[![Build Status](https://github.com/Shopify/quilt/workflows/Node-CI/badge.svg?branch=main)](https://github.com/Shopify/quilt/actions?query=workflow%3ANode-CI)
4[![Build Status](https://github.com/Shopify/quilt/workflows/Ruby-CI/badge.svg?branch=main)](https://github.com/Shopify/quilt/actions?query=workflow%3ARuby-CI)
5[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE.md) [![npm version](https://badge.fury.io/js/graphql-typescript-definitions.svg)](https://badge.fury.io/js/graphql-typescript-definitions.svg) {{#if usedInBrowser}} [![npm bundle size (minified + gzip)](https://img.shields.io/bundlephobia/minzip/graphql-typescript-definitions.svg)](https://img.shields.io/bundlephobia/minzip/graphql-typescript-definitions.svg) {{/if}}
6
7Generate TypeScript definition files from .graphql documents.
8
9## Installation
10
11```bash
12$ yarn add graphql-typescript-definitions
13```
14
15## Usage
16
17This package will generate matching `.d.ts` files for each `.graphql` file you specify. It will generate types in the following format:
18
19- A default export for the type that will be generated by a GraphQL loader (GraphQL’s `DocumentNode` type, but augmented as `graphql-typed`’s `DocumentNode` which includes additional type details about the operation).
20
21- An interface for each query, mutation, and fragment, named `<OpertionName><Query | Mutation | Fragment>Data`. For example, `query Home {}` becomes `export interface HomeQueryData {}`.
22
23- A namespace for each operation that includes any nested types. Nested types are named in pascal case using their keypath from the root of the operation. For example, if we imagine the following GraphQL schema (using the [GraphQL IDL](https://www.graph.cool/docs/faq/graphql-idl-schema-definition-language-kr84dktnp0/)):
24
25 ```graphql
26 type Person {
27 name: String!
28 relatives: [Person!]!
29 }
30
31 type Query {
32 person: Person
33 }
34 ```
35
36 and the following query:
37
38 ```graphql
39 query Someone {
40 person {
41 name
42 relatives {
43 name
44 }
45 }
46 }
47 ```
48
49 The following exports would be generated:
50
51 ```typescript
52 export interface SomeoneQueryData {
53 person?: SomeoneQueryData.Person | null;
54 }
55
56 export namespace SomeoneQueryData {
57 export interface Person {
58 name: string;
59 relatives: SomeoneQueryData.PersonRelatives[];
60 }
61
62 export interface PersonRelatives {
63 name: string;
64 }
65 }
66 ```
67
68 This allows you to use the full query’s type, as well as any of the subtypes that make up that query type. This is particularly useful for list or nullable types, where you can directly the access the underlying type without any additional help from TypeScript:
69
70 ```typescript
71 import someoneQueryDocument, {SomeoneQueryData} from './Someone.graphql';
72
73 let data: SomeoneQueryData;
74 let person: SomeoneQueryData.Person;
75 ```
76
77### Operation
78
79On startup this tool performs the following actions:
80
81- Loads all schemas
82- Extracts all enums, input objects, and custom scalars as schema types
83- Writes the schema types to `types.ts` (or `${projectName}-types.ts` for named projects)
84 - Written in directory provided by `--schema-types-path` argument
85 - Override `--schema-types-path` per project with the `schemaTypesPath` extension
86
87### Configuration
88
89This tool reads schema information from a [`.graphqlconfig`](https://github.com/Shopify/graphql-tools-web/tree/main/packages/graphql-tool-utilities#configuration) file in the project root.
90
91#### Examples
92
93A project configuration with a `schemaTypesPath` override
94
95```json
96{
97 "schemaPath": "build/schema.json",
98 "includes": ["app/**/*.graphql"],
99 "extensions": {
100 "schemaTypesPath": "app/bar/types/graphql"
101 }
102}
103```
104
105### Type Generation
106
107#### Nullability
108
109As demonstrated in the root `person` field in the example above, nullable fields are represented as optional types, in a union with `null`. Nullable items in list fields (i.e., `[Person]!`) are represented as a union type with `null`.
110
111#### Interfaces and Unions
112
113Interface an union fields are represented as union types in cases where there are spreads that could result in different fields on different concrete types. The type names for these cases are named the same as the default naming (pascal case version of the keypath for the field), but with the type condition appended to the end. All cases not covered by fragments are extracted into a type with a postpended `Other` name.
114
115```graphql
116# Schema
117interface Named {
118 name: String!
119}
120
121type Person implements Named {
122 name: String!
123 occupation: String
124}
125
126type Dog implements Named {
127 name: String!
128 legs: Int!
129}
130
131type Cat implements Named {
132 name: String!
133 livesLeft: Int!
134}
135
136type Horse implements Named {
137 name: String!
138 topSpeed: Float!
139}
140
141type Query {
142 named: Named
143}
144```
145
146```graphql
147# Query
148query SomeNamed {
149 named {
150 name
151 ... on Person {
152 occupation
153 }
154 ... on Dog {
155 legs
156 }
157 }
158}
159```
160
161```typescript
162// generated types
163export interface SomeNamedData {
164 named?:
165 | SomeNamedData.NamedPerson
166 | SomeNamedData.NamedDog
167 | SomeNamedData.NamedOther
168 | null;
169}
170
171export namespace SomeNamedData {
172 export interface NamedPerson {
173 __typename: 'Person';
174 name: string;
175 occupation?: string | null;
176 }
177 export interface NamedDog {
178 __typename: 'Dog';
179 name: string;
180 legs: number;
181 }
182 export interface NamedOther {
183 __typename: 'Cat' | 'Horse';
184 name: string;
185 }
186}
187```
188
189Note that the above example assumes that you specify the `--add-typename` argument. These types are only useful when a typename is included either explicitly or with this argument, as otherwise there is no simple way for TypeScript to disambiguate the union type.
190
191#### Schema Types
192
193Input types (enums, input objects, and custom scalars) are generated once, in a central location, and imported within each typing file. You can use these definitions to reference the schema types in other application code as well; in particular, GraphQL enums are turned into corresponding TypeScript `enum`s. The schema types directory is specified using the `--schema-types-path` argument (detailed below), and the format for the generated enums can be specified using the `--enum-format` option.
194
195### CLI
196
197```sh
198graphql-typescript-definitions --schema-types-path app/types
199```
200
201As noted above, the configuration of your schema and GraphQL documents is done via a `.graphqlconfig` file, as this allows configuration to shared between tools. The CLI does support a few additional options, though:
202
203- `--schema-types-path`: specifies a directory to write schema types (**REQUIRED**)
204- `--watch`: watches the include globbing patterns for changes and re-processes files (default = `false`)
205- `--cwd`: run tool for `.graphqlconfig` located in this directory (default = `process.cwd()`)
206- `--add-typename`: adds a `__typename` field to every object type (default = `true`)
207- `--export-format`: species the shape of values exported from `.graphql` files (default = `document`)
208 - Options: `document` (exports a `graphql-typed` `DocumentNode`), `simple` (exports a `graphql-typed` `SimpleDocument`)
209- `--enum-format`: specifies output format for enum types (default = `undefined`)
210 - Options: `camel-case`, `pascal-case`, `snake-case`, `screaming-snake-case`
211 - `undefined` results in using the unchanged name from the schema (verbatim)
212- `--custom-scalars`: specifies custom types to use in place of scalar types in your GraphQL schema. See below for details.
213
214#### Examples
215
216```sh
217# run tool for .graphqlconfig in current directory, produces ./app/graphql/types
218graphql-typescript-definitions --schema-types-path app/graphql/types
219
220# run watcher for .graphqlconfig in current directory, produces ./app/graphql/types
221graphql-typescript-definitions --schema-types-path app/graphql/types --watch
222
223# run tool for .graphqlconfig in a child directory, produces ./src/app/graphql/types
224graphql-typescript-definitions --cwd src --schema-types-path app/graphql/types
225```
226
227#### `--custom-scalars`
228
229By default, all custom scalars are exported as an alias for `string`. You can export a different type for these scalars by passing in a `--custom-scalars` option. This option is a JSON-serialized object that specifies what custom type to import from a package and re-export as the type for that scalar. For example, assuming the following schema:
230
231```graphql
232scalar HtmlString
233```
234
235You may want a custom TypeScript type for any field of this GraphQL type (for example, to restrict functions to use only this type, and not any arbitrary string). Assuming you have an installed npm package by the name of `my-custom-type-package`, and this package exports a named `SafeString` type, you could pass the following `--custom-scalars` option:
236
237```sh
238yarn run graphql-typescript-definitions --schema-path 'build/schema.json' --schema-types-path 'src/schema' --custom-scalars '{"HtmlString": {"name": "SafeString", package: "my-custom-type-package"}}'
239```
240
241With this configuration, your custom scalar would be exported roughly as follows:
242
243```ts
244import {SafeString} from 'my-custom-type-package';
245export type HtmlString = SafeString;
246```
247
248You can also use built-in types by simply leaving off the `package` property:
249
250```sh
251yarn run graphql-typescript-definitions --schema-path 'build/schema.json' --schema-types-path 'src/schema' --custom-scalars '{"Seconds": {"name": "number"}}'
252```
253
254This will produce a simple type alias:
255
256```ts
257export type Seconds = number;
258```
259
260### Node
261
262```js
263const {Builder} = require('graphql-typescript-definitions');
264
265const builder = new Builder({
266 schemaTypesPath: 'app/graphql/types',
267});
268
269builder.on('build', build => {
270 // See the source file for details on the shape of the object returned here
271 console.log(build);
272});
273
274builder.on('error', error => {
275 console.error(error);
276});
277
278// Optionally, you can pass {watch: true} here to re-run on changes
279builder.run();
280```
281
282As with the CLI, you can pass options to customize the build and behavior:
283
284- `watch`
285- `enumFormat` (use the exported `EnumFormat` enum)
286- `graphQLFiles`
287- `schemaPath`
288- `schemaTypesPath`
289- `customScalars`
290- `config` (custom `GraphQLConfig` instance)
291
292#### Customizing filesystem interactions
293
294In projects with thousands of GraphQL documents, `Builder` may take a few seconds to reach its ready state. Additionally, very large projects with deep file trees may exceed Node's default memory limits (especially in non-MacOS environments). To allow project/environment-specific customization, builders accept a [`GraphQLFilesystem`](./src/filesystem/graphql-filesystem.ts) object.
295
296```js
297const {
298 AbstractGraphQLFilesystem,
299 Builder,
300} = require('graphql-typescript-definitions');
301
302class CustomFilesystem extends AbstractGraphQLFilesystem {
303 watch(config) {
304 // Set up filesystem watchers, and emit change:schema / change:document / delete:document events.
305 }
306
307 getGraphQLProjectIncludedFilePaths(projectConfig) {
308 // Find all .graphql documents for the given project.
309 }
310
311 dispose() {
312 // Release any resources held by watchers.
313 }
314}
315
316const builder = new Builder({
317 graphQLFilesystem: new CustomFilesystem(),
318});
319
320builder.run({watch: true});
321```