Graphql Flow Generation
This is a tool for generating flow types from graphql queries in javascript frontends.
Using as a CLI tool
Write a config file, with the following options:
{
"schemaFilePath": "../some/schema-file.json",
"excludes": ["\\bsome-thing", "_test.jsx?$"],
"options": {
"scalars": {
"JSONString": "string"
}
}
}
Then run from the CLI, like so:
$ graphql-flow path/to/config.json
Files will be discovered relative to the current working directory.
To specify what file should be checked, pass them in as subsequent cli arguments.
Options (for the cli 'options' config item, or when running from jest):
type Options = {
strictNullability: boolean = true,
readOnlyArray: boolean = true,
scalars: {[key: string]: 'string' | 'boolean' | 'number'}
generatedDirectory: string = '__generated__',
splitTypes?: boolean,
pragma?: string,
loosePragma?: string,
ignorePragma?: string,
exportAllObjectTypes?: boolean,
typeFileName?: string,
}
Using from jest
You can also use jest to do the heavy lifting, running all of your code and collecting queries
by mocking out the graphql-tag
function itself. This requires that all graphql operations are
defined at the top level (no queries defined in functions or components, for example), but does
support complicated things like returning a fragment from a function (which is probably
not a great idea code-style-wise anyway).
jest-setup.js
Add the following snippet to your jest-setup.js
if (process.env.GRAPHQL_FLOW) {
jest.mock('graphql-tag', () => {
const introspectionData = jest.requireActual(
'./server-introspection-response.json',
);
return jest.requireActual('../tools/graphql-flow/jest-mock-graphql-tag.js')(
introspectionData,
{
pragma: '# @autogen\n',
loosePragma: '# @autogen-loose\n',
}
);
});
}
That will mock out the graphql-tag
function, so that everywhere you call 'gqlsome-query
', we'll pick it up and
generate a type for it! As written, the mocking only happens if the GRAPHQL_FLOW
env variable is set, so that it won't run every time.
By default, all queries are processed. To have them 'opt-in', use the pragma
and loosePragma
options. Queries with loosePragma
in the source will have types generated that ignore nullability.
Ensure all files with queries get processed by jest
Then you'll want to make a pseudo-'test' that makes sure to 'require' all of the files that define queries, so that
jest will process them and our mock will see them.
import {findGraphqlTagReferences} from '../tools/graphql-flow/find-files-with-gql';
import path from 'path';
if (process.env.GRAPHQL_FLOW) {
findGraphqlTagReferences(path.join(__dirname, '..')).forEach(name => {
require(name);
});
it('should have found queries', () => {
const gql = require('graphql-tag')
expect(gql.collectedQueries.length).toBeGreaterThan(0)
})
} else {
it(`not generating graphql types because the env flag isn't set`, () => {
})
}
Run that test to generate the types!
You can add a script to package.json, like so:
"scripts": {
"generate-types": "env GRAPHQL_FLOW=1 jest generate-types.test.js"
}
And then yarn generate-types
or npm run generate-types
gets your types generated!
🚀
Introspecting your backend's graphql schema
Here's how to get your backend's schema in the way that this tool expects, using the builtin 'graphql introspection query':
import {getIntrospectionQuery} from 'graphql';
import fs from 'fs';
import fetch from 'node-fetch';
const query = getIntrospectionQuery({descriptions: true}),
const response = await fetch(`https://my-backend.com`, {
method: 'POST',
body: query,
headers: {
'X-header-that-allows-arbitrary-queries': 'my-secret-key',
},
contentType: 'application/json',
});
const fullResponse = await response.json();
fs.writeFileSync('./server-introspection-response.json', JSON.stringify(fullResponse.data, null, 2));