Skip to content

Commit 3e709b9

Browse files
committed
fix: refactor so that fetching type metadata without an introspection query is possible
BREAKING CHANGE: the following old way of prefetching the metadata is no longer supported: ```js import getSchemaTypes from 'apollo-magic-refetch/getSchemaTypes' import client from './wherever/you/create/your/apollo/client' getSchemaTypes(client) ``` Instead, do the following: ```js import refetch from 'apollo-magic-refetch' import client from './wherever/you/create/your/apollo/client' refetch.fetchTypeMetadata(client) ``` Since it is common to disable introspection queries in production, I have refactored to support fetching the type metadata by other means. For instance, you could set up the following route on your server: ```js import { execute } from 'graphql' import schema from './path/to/your/graphql/schema' import express from 'express' import { typesQuery } from 'apollo-magic-refetch' const app = express() const typeMetadataPromise = execute(schema, typesQuery) app.get('/graphql/refetchTypeMetadata', (req, res) => { typeMetadataPromise.then(data => res.json(data)) }) ``` And then pass this data to `refetch.setTypeMetadata` **before you ever call `refetch()`**: ```js import refetch from 'apollo-magic-refetch' // accepts a promise that resolves to the graphql execution result. refetch.setTypeMetadata( fetch('/graphql/refetchTypeMetadata').then(res => res.json()) ) ```
1 parent daa1408 commit 3e709b9

File tree

4 files changed

+95
-21
lines changed

4 files changed

+95
-21
lines changed

README.md

+32-3
Original file line numberDiff line numberDiff line change
@@ -68,16 +68,45 @@ If you are not using a bundler that supports the `modules.root` property in
6868
`apollo-magic-refetch` uses type metadata that it must fetch from GraphQL.
6969
If your schema is large enough it may be a prohibitive amount of metadata.
7070
`refetch` operations will be delayed until this metadata is fetched.
71-
To prefetch this metadata, do:
71+
To prefetch this metadata via a GraphQL introspection query, do:
7272

7373
```js
7474
import client from './wherever/you/create/your/apollo/client'
75-
import getSchemaTypes from 'apollo-magic-refetch/getSchemaTypes'
75+
import refetch from 'apollo-magic-refetch'
7676

7777
// initiate the prefetch
78-
getSchemaTypes(client)
78+
refetch.fetchTypeMetadata(client)
7979
```
8080

81+
If your server forbids introspection queries, you will have to fetch it by
82+
other means. For instance, you could set up the following route on your server:
83+
84+
````js
85+
import { execute } from 'graphql'
86+
import schema from './path/to/your/graphql/schema'
87+
import express from 'express'
88+
import { typesQuery } from 'apollo-magic-refetch'
89+
90+
const app = express()
91+
92+
const typeMetadataPromise = execute(schema, typesQuery)
93+
94+
app.get('/graphql/refetchTypeMetadata', (req, res) => {
95+
typeMetadataPromise.then(data => res.json(data))
96+
})```
97+
98+
And then pass this data to `refetch.setTypeMetadata` **before you ever call
99+
`refetch()`**:
100+
101+
```js
102+
import refetch from 'apollo-magic-refetch'
103+
104+
// accepts a promise that resolves to the graphql execution result.
105+
refetch.setTypeMetadata(
106+
fetch('/graphql/refetchTypeMetadata').then(res => res.json())
107+
)
108+
````
109+
81110
## Handling Deletions
82111

83112
In this example, the `__typename` of the object being deleted is `Device` and it

src/getSchemaTypes.js

+6-14
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// @flow
22

3-
import type { ApolloClient } from 'apollo-client'
4-
import typesQuery from './typesQuery'
3+
import type { TypeMetadata } from './typesQuery'
54

65
export type RawField = {
76
name: string,
@@ -31,8 +30,6 @@ export type Type = {
3130

3231
export type Types = { [name: string]: Type }
3332

34-
let promise: ?Promise<Types>
35-
3633
function convertRawField({ name, type }: RawField): Field {
3734
return ({ name, type: convertRawType(type) }: any)
3835
}
@@ -88,18 +85,13 @@ export function linkTypes(rawTypes: Array<RawType>): Types {
8885
return types
8986
}
9087

91-
async function getSchemaTypes(client: ApolloClient<any>): Promise<Types> {
88+
export default async function getSchemaTypes(
89+
fetchTypeMetadata: () => Promise<TypeMetadata>
90+
): Promise<Types> {
9291
const {
9392
data: {
9493
__schema: { types },
9594
},
96-
} = await client.query({ query: typesQuery })
97-
return linkTypes(types)
98-
}
99-
100-
export default function getSchemaTypesOnce(
101-
client: ApolloClient<any>
102-
): Promise<Types> {
103-
if (!promise) promise = getSchemaTypes(client)
104-
return promise
95+
} = await fetchTypeMetadata()
96+
return linkTypes((types: any))
10597
}

src/index.js

+23-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import { ApolloClient } from 'apollo-client'
44
import getSchemaTypes from './getSchemaTypes'
55
import type { Types } from './getSchemaTypes'
66
import doesQueryContain from './doesQueryContain'
7-
export { default as typesQuery } from './typesQuery'
7+
import typesQuery, { type TypeMetadata } from './typesQuery'
8+
export { typesQuery }
9+
export type { TypeMetadata }
810

911
function normalizePredicate(
1012
predicate: any,
@@ -29,18 +31,20 @@ function every<T>(
2931
return true
3032
}
3133

34+
let typesPromise: ?Promise<Types> = null
35+
3236
export default async function refetch(
3337
client: mixed,
3438
typenameOrTerms: string | $ReadOnlyArray<Term>,
3539
predicate?: ?any,
3640
idField?: string
3741
): Promise<any> {
38-
if (!(client instanceof ApolloClient))
42+
if (!(client instanceof ApolloClient)) {
3943
throw new Error(
4044
`client must be an ApolloClient, instead got: ${String(client)}`
4145
)
42-
43-
const types: Types = await getSchemaTypes(client)
46+
}
47+
const types: Types = await refetch.fetchTypeMetadata(client)
4448

4549
let terms
4650
if (typeof typenameOrTerms === 'string') {
@@ -80,3 +84,18 @@ export default async function refetch(
8084
}
8185
await promises
8286
}
87+
88+
refetch.fetchTypeMetadata = async function(
89+
client: ApolloClient<any>
90+
): Promise<Types> {
91+
if (!typesPromise) {
92+
refetch.setTypeMetadata(client.query({ query: typesQuery }))
93+
}
94+
// istanbul ignore next
95+
if (!typesPromise) throw new Error('this should never happen')
96+
return await typesPromise
97+
}
98+
99+
refetch.setTypeMetadata = (metadata: Promise<TypeMetadata> | TypeMetadata) => {
100+
typesPromise = getSchemaTypes(async () => await metadata)
101+
}

src/typesQuery.js

+34
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,37 @@ export default gql`
2929
}
3030
}
3131
`
32+
33+
type __TypeKind =
34+
| 'SCALAR'
35+
| 'OBJECT'
36+
| 'INTERFACE'
37+
| 'UNION'
38+
| 'ENUM'
39+
| 'INPUT_OBJECT'
40+
| 'LIST'
41+
| 'NON_NULL'
42+
43+
export type TypeMetadata = {
44+
data: {
45+
__schema: {
46+
types: Array<{
47+
name: ?string,
48+
fields: ?Array<{
49+
name: string,
50+
type: {
51+
name: ?string,
52+
kind: __TypeKind,
53+
ofType: ?{
54+
name: ?string,
55+
kind: __TypeKind,
56+
ofType: ?{
57+
name: ?string,
58+
},
59+
},
60+
},
61+
}>,
62+
}>,
63+
},
64+
},
65+
}

0 commit comments

Comments
 (0)