Skip to content

Commit 0f2fffa

Browse files
committed
Add README
1 parent c5f1316 commit 0f2fffa

File tree

1 file changed

+321
-0
lines changed

1 file changed

+321
-0
lines changed

README.md

Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
# apollo-typed-documents
2+
3+
Provides codegen plugins (https://graphql-code-generator.com/) for type safe apollo documents.
4+
5+
It allows functions to accept a generic `TypedDocumentNode<TVariables, TData>` so that types of other arguments or the return type can be inferred.
6+
7+
It is helpful for typescript projects but also if used only within an IDE, e.g. it works extremely well with VSCode (uses typescript behind the scenes).
8+
9+
```sh
10+
$ yarn add apollo-typed-documents
11+
```
12+
13+
## codegenTypedDocuments
14+
15+
Generates TypeScript typings for `.graphql` files.
16+
17+
Similar to `@graphql-codegen/typescript-graphql-files-modules` (https://graphql-code-generator.com/docs/plugins/typescript-graphql-files-modules).
18+
19+
The difference is that is uses generic types, so that you have type safety with Apollo (e.g. `useQuery` / `useMutation`).
20+
21+
### Install
22+
23+
`codegen.yml`:
24+
25+
```yml
26+
schema: http://localhost:8000/graphql/
27+
documents: ./src/**/*.gql
28+
generates:
29+
./src/codegenTypedDocuments.d.ts:
30+
plugins:
31+
- apollo-typed-documents/lib/codegenTypedDocuments
32+
config:
33+
typesModule: "@codegen-types"
34+
./src/codegenTypes.ts:
35+
plugins:
36+
- typescript
37+
- typescript-operations
38+
```
39+
40+
`tsconfig.json`:
41+
42+
```json
43+
{
44+
"compilerOptions": {
45+
"baseUrl": ".",
46+
"paths": {
47+
"@codegen-types": ["./codegenTypes.ts"],
48+
"@apollo/react-hooks": [
49+
"./node_modules/apollo-typed-documents/lib/reactHooks.d.ts"
50+
]
51+
}
52+
}
53+
}
54+
```
55+
56+
`@codegen-types` points to the output of `typescript-operations` codegen plugin.
57+
58+
This alias is required, because in ambient module declarations (`.d.ts`) only non relative imports are allowed.
59+
60+
`@apollo/react-hooks` overrides the types to have generic hooks in your code: [reference](src/reactHooks.ts)
61+
62+
### Example
63+
64+
`passwordChangeMutation.gql`:
65+
66+
```graphql
67+
mutation passwordChange(
68+
$oldPassword: String!
69+
$newPassword1: String!
70+
$newPassword2: String!
71+
) {
72+
passwordChange(
73+
oldPassword: $oldPassword
74+
newPassword1: $newPassword1
75+
newPassword2: $newPassword2
76+
) {
77+
success
78+
errors
79+
}
80+
}
81+
```
82+
83+
`codegenTypedDocuments.d.ts` (generated):
84+
85+
```ts
86+
declare module "*/passwordChangeMutation.gql" {
87+
import { TypedDocumentNode } from "apollo-typed-documents";
88+
import {
89+
PasswordChangeMutation,
90+
PasswordChangeMutationVariables,
91+
} from "@codegen-types";
92+
export const passwordChange: TypedDocumentNode<
93+
PasswordChangeMutationVariables,
94+
PasswordChangeMutation
95+
>;
96+
export default passwordChange;
97+
}
98+
```
99+
100+
`changePassword.js`:
101+
102+
```js
103+
import passwordChangeMutation from "./passwordChangeMutation.gql";
104+
import { useMutation } from "@apollo/react-hooks";
105+
106+
const ChangePassword = () => {
107+
const [changePassword] = useMutation(
108+
passwordChangeMutation,
109+
{
110+
onCompleted: data => {
111+
// Type of `data` is inferred (PasswordChangeMutation)
112+
// IDE / tsc complains invalid access of data
113+
// IDE can provide code completion (IntelliSense)
114+
if (data.passwordChange?.success) {
115+
history.push("/")
116+
}
117+
}
118+
}
119+
)
120+
121+
useEffect(() => {
122+
// Type of variables is inferred (PasswordChangeMutationVariables)
123+
verifyAccount({ variables: { token } })
124+
})
125+
126+
return ...
127+
}
128+
```
129+
130+
### Notes for `create-react-app` users
131+
132+
`create-react-app` uses `graphql.macro` for loading of `.graphql` files (https://create-react-app.dev/docs/loading-graphql-files/).
133+
134+
Because the `codegenTypedDocuments` plugin generates ambient module declarations for those `.graphql` files, they must be imported as regular modules (`commonjs`), otherwise typescript can't know its type.
135+
136+
You can use the babel plugin `babel-plugin-import-graphql`, but then you need to either `eject` or use `react-app-rewired`/`customize-cra`.
137+
138+
Follow install instructions for `react-app-rewired`/`customize-cra`:
139+
140+
- https://github.com/timarney/react-app-rewired/
141+
- https://github.com/arackaf/customize-cra
142+
143+
Install `babel-plugin-import-graphql`
144+
145+
```
146+
$ yarn add babel-plugin-import-graphql
147+
```
148+
149+
Add to `.babelrc`:
150+
151+
```json
152+
{
153+
"presets": ["react-app"],
154+
"plugins": ["babel-plugin-import-graphql"]
155+
}
156+
```
157+
158+
Also, if you have a `create-react-app` project without typescript, the `tsconfig.json` must not be placed in your app root folder, because `create-react-app` uses this file to detect a typescript project [reference](https://github.com/facebook/create-react-app/blob/v3.4.1/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js#L49).
159+
160+
All `.ts`/`.d.ts` files must be outside of your `src` folder [reference](https://github.com/facebook/create-react-app/blob/v3.4.1/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js#L50)
161+
162+
A solution is to put the `tsconfig.json` in the parent folder:
163+
164+
```
165+
tsconfig.json
166+
backend/
167+
frontend/
168+
codegen.yml
169+
codegenTypedDocuments.d.ts
170+
codegenTypes.ts
171+
src/
172+
apolloMock.js
173+
...
174+
```
175+
176+
An example `tsconfig.json`:
177+
178+
```json
179+
{
180+
"comment": "File is in root folder so that create-react-app doesn't detect a typescript app",
181+
"compilerOptions": {
182+
"rootDir": "frontend",
183+
"typeRoots": ["frontend/node_modules/@types"],
184+
"baseUrl": "frontend",
185+
"paths": {
186+
"@codegen-types": ["codegenTypes.ts"],
187+
"@apollo/react-hooks": [
188+
"node_modules/apollo-typed-documents/lib/reactHooks.d.ts"
189+
]
190+
},
191+
"allowJs": true,
192+
"checkJs": true,
193+
"noEmit": true,
194+
"jsx": "react",
195+
"esModuleInterop": true,
196+
"moduleResolution": "node",
197+
"alwaysStrict": true,
198+
"strictNullChecks": true,
199+
"target": "ES2018"
200+
},
201+
"include": [
202+
"frontend/src/**/*",
203+
"frontend/*.ts",
204+
"frontend/*.d.ts",
205+
"frontend/node_modules/react-scripts/lib/react-app.d.ts",
206+
"frontend/node_modules/babel-plugin-react-intl-auto/types.d.ts"
207+
]
208+
}
209+
```
210+
211+
## codegenApolloMock
212+
213+
Creates a helper method to easily create mocks for Apollo `MockedProvider` (https://www.apollographql.com/docs/react/api/react-testing/#mockedprovider).
214+
215+
The returned object is guaranteed to conform to the GraphQL Schema [reference](src/createApolloMock.ts).
216+
217+
However it will only contain the fields which are selected in the query / mutation.
218+
219+
For fields which are required but not given, it will return a default value (e.g. `"UserType-id"`).
220+
221+
Works for any nested selections.
222+
223+
When used together with `codegenTypedDocuments` the test data is type checked (type inference).
224+
225+
### Install
226+
227+
`codegen.yml`
228+
229+
```yml
230+
schema: http://localhost:8000/graphql/
231+
documents: ./src/**/*.gql
232+
generates:
233+
./src/apolloMock.js:
234+
plugins:
235+
- apollo-typed-documents/lib/codegenApolloMock
236+
```
237+
238+
### Example
239+
240+
`schema.graphql`:
241+
242+
```graphql
243+
type TaskTypeConnection {
244+
edges: [TaskTypeEdge]!
245+
}
246+
247+
type TaskTypeEdge {
248+
node: TaskType!
249+
cursor: String!
250+
}
251+
252+
type TaskType {
253+
id: UUID!
254+
slug: String!
255+
description: String
256+
}
257+
258+
type Query {
259+
tasks(after: String, first: Int): TaskTypeConnection!
260+
}
261+
```
262+
263+
`tasksQuery.graphql`:
264+
265+
```graphql
266+
query tasks($after: String, $first: Int) {
267+
tasks(after: $after, first: $first) {
268+
edges {
269+
node {
270+
id
271+
slug
272+
description
273+
}
274+
}
275+
}
276+
}
277+
```
278+
279+
`tasks.test.js`:
280+
281+
```js
282+
import apolloMock from "./apolloMock"
283+
import tasksQuery from "./tasksQuery.gql";
284+
285+
expect(apolloMock(tasksQuery, {}, {})).equals({
286+
request: {
287+
query: tasksQuery,
288+
variables
289+
},
290+
result: {
291+
data: {
292+
tasks: {
293+
edges: []
294+
}
295+
}
296+
}
297+
})
298+
299+
expect(apolloMock(tasksQuery, {}, { tasks: { edges: [{}]})).equals({
300+
request: {
301+
query: tasksQuery,
302+
variables
303+
},
304+
result: {
305+
data: {
306+
tasks: {
307+
edges: [
308+
{
309+
node: {
310+
id: "TaskType-id",
311+
slug: "TaskType-slug",
312+
description: null
313+
},
314+
cursor: "TaskTypeEdge-cursor"
315+
}
316+
]
317+
}
318+
}
319+
}
320+
})
321+
```

0 commit comments

Comments
 (0)