Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/codemodsquad/graphql-typegen
JSCodeshift transform that inserts Flow types generated from GraphQL documents in template string literals and your GraphQL schema
https://github.com/codemodsquad/graphql-typegen
apollo codemods flow flowtype graphql jscodeshift
Last synced: 3 months ago
JSON representation
JSCodeshift transform that inserts Flow types generated from GraphQL documents in template string literals and your GraphQL schema
- Host: GitHub
- URL: https://github.com/codemodsquad/graphql-typegen
- Owner: codemodsquad
- License: mit
- Created: 2020-02-02T05:55:31.000Z (about 5 years ago)
- Default Branch: master
- Last Pushed: 2024-05-10T17:02:25.000Z (10 months ago)
- Last Synced: 2024-11-14T17:48:10.935Z (3 months ago)
- Topics: apollo, codemods, flow, flowtype, graphql, jscodeshift
- Language: TypeScript
- Homepage:
- Size: 728 KB
- Stars: 6
- Watchers: 2
- Forks: 1
- Open Issues: 10
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
- awesome-list - graphql-typegen
README
# graphql-typegen
[](https://circleci.com/gh/codemodsquad/graphql-typegen)
[](https://codecov.io/gh/codemodsquad/graphql-typegen)
[](https://github.com/semantic-release/semantic-release)
[](http://commitizen.github.io/cz-cli/)
[](https://badge.fury.io/js/graphql-typegen)JSCodeshift transform that inserts Flow types generated from GraphQL documents in template string literals and your GraphQL schema, and wires up the generated types to Apollo
# Table of Contents
- [Example](#example)
- [Before](#before)
- [After](#after)
- [Rationale](#rationale)
- [Importing generated types from external files is annoying](#importing-generated-types-from-external-files-is-annoying)
- [`graphql-codegen` outputs messy types for documents](#graphql-codegen-outputs-messy-types-for-documents)
- [I want to extract parts of the query with their own type aliases](#i-want-to-extract-parts-of-the-query-with-their-own-type-aliases)
- [Interpolation in GraphQL tagged template literals](#interpolation-in-graphql-tagged-template-literals)
- [Automatically adding type annotations to `Query`, `Mutation`, `useQuery`, `useMutation`, and `useSubscription`](#automatically-adding-type-annotations-to-query-mutation-usequery-usemutation-and-usesubscription)
- [Configuration](#configuration)
- [`schemaFile` / `server`](#schemafile--server)
- [`tagName` (default: `gql`)](#tagname-default-gql)
- [`addTypename` (default: `true`)](#addtypename-default-true)
- [`objectType` (default: `ambiguous`)](#objecttype-default-ambiguous)
- [`useReadOnlyTypes` (default: `false`)](#usereadonlytypes-default-false)
- [`useFunctionTypeArguments` (default: `true`)](#usefunctiontypearguments-default-true)
- [`external as`](#external-as-)
- [`extract [as ]`](#extract-as-)
- [CLI Usage](#cli-usage)
- [Node.js API](#nodejs-api)# Example
## Before
```js
import gql from 'graphql-tag'const fragment = gql`
fragment CharacterFields on Character {
name
appearsIn
}
`const fragment2 = gql`
${fragment}
fragment CharacterAndFriends on Character {
...CharacterFields
friends {
...CharacterFields
}
}
`const query = gql`
${fragment2}
query Test($id: ID!) {
character(id: $id) {
id
...CharacterAndFriends
}
}
`
```## After
```js
import gql from 'graphql-tag'const fragment = gql`
fragment CharacterFields on Character {
name
appearsIn
... on Human {
mass
}
... on Droid {
primaryFunction
}
}
`// @graphql-typegen auto-generated
type CharacterFields =
| {
name: string,
appearsIn: Array('NEWHOPE' | 'EMPIRE' | 'JEDI')>,
__typename: 'Human',
mass: ?number,
}
| {
name: string,
appearsIn: Array('NEWHOPE' | 'EMPIRE' | 'JEDI')>,
__typename: 'Droid',
primaryFunction: ?string,
}const fragment2 = gql`
${fragment}
fragment CharacterAndFriends on Character {
...CharacterFields
friends {
...CharacterFields
}
}
`// @graphql-typegen auto-generated
type CharacterAndFriends = {
__typename: 'Human' | 'Droid',
friends: ?Array,
} & CharacterFieldsconst query = gql`
${fragment2}
query Test($id: ID!) {
character(id: $id) {
id
...CharacterAndFriends
}
}
`// @graphql-typegen auto-generated
type TestQueryVariables = { id: string }// @graphql-typegen auto-generated
type TestQueryData = {
__typename: 'Query',
character: ?({
__typename: 'Human' | 'Droid',
id: string,
} & CharacterAndFriends),
}
```# Rationale
With established GraphQL code generators out there (`apollo-tooling` and `graphql-codegen`)
you might wonder why I decided to make my own instead. There are several reasons...## Importing generated types from external files is annoying
Both `graphql-codegen` and `apollo-tooling` output types in separate files from your
GraphQL documents. This means you have to pick a globally unique identifier for
each GraphQL operation. Dealing with global namespaces is always a pain in the ass.
It also means you have to insert an import statement. While I also have [my own tool
that's pretty damn good at automatic imports](https://github.com/jedwards1211/dude-wheres-my-module),
it would still be an annoying extra step.`graphql-typegen` just inserts the generated types after the GraphQL
tagged template literals in my code, so I don't have to worry about picking
globally unique operation names or adding imports.## `graphql-codegen` outputs messy types for documents
Example:
```graphql
query Test($id: ID!) {
user(id: $id) {
id
username
}
}
```Output:
```js
type $Pick = $ObjMapi<
Keys,
(k: Key) => $ElementType
>export type TestQueryVariables = {
id: $ElementType,
}export type TestQuery = {
...{ __typename?: 'Query' },
...{|
user: ?{
...{ __typename?: 'User' },
...$Pick,
},
|},
}
```Pretty awful, huh? It's questionable if this even works properly in Flow; I've seen bugs with spreads inside inexact/ambiguous object types in the past.
`graphql-typegen` would output:
```js
// @graphql-typegen auto-generated
type TestQueryData = {
__typename: 'Query',
user: {
__typename: 'User',
id: ID,
username: string,
},
}
```## I want to extract parts of the query with their own type aliases
Take the query example above. Let's say I need to refer to the `user` type in `TestQuery`
above. All I have to do is add this comment:```graphql
query Test($id: ID!) {
# @graphql-typegen extract
user(id: $id) {
id
username
}
}
```And `graphql-typegen` will output:
```js
// @graphql-typegen auto-generated
type User = {
__typename: 'User',
id: ID,
username: string,
}// @graphql-typegen auto-generated
type TestQueryData = {
__typename: 'Query',
user: User,
}
```This is much easier than `type User = $PropertyType`,
especially for extracting types that are more than one level deep in the query
(`$PropertyType<$PropertyType, 'bar'>` would be pretty awful)## Interpolation in GraphQL tagged template literals
At the moment, [`apollo-tooling` doesn't support interpolation in tagged template literals](https://github.com/apollographql/apollo-tooling/issues/182).
This is a pretty crucial for sharing fragments between queries and mutations, which is, you know, common.`graphql-typegen` supports this:
```js
const UserFragment = gql`
fragment UserFields on User {
id
username
}
`const userQuery = gql`
${UserFragment}
query user($id: ID!) {
user(id: $id) {
...UserFields
}
}
`const updateUserMutation = gql`
${UserFragment}
mutation updateUser($id: ID!, $values: UpdateUser!) {
updateUser(id: $id, values: $values) {
...UserFields
}
}
`
```Output:
```js
// @graphql-typegen auto-generated
type UserFields = {
id: ID,
username: string,
}// @graphql-typegen auto-generated
type UserQueryData = {
__typename: 'Query',
user: { __typename: 'User' } & UserFields,
}// @graphql-typegen auto-generated
type UserQueryVariables = {
id: ID,
}// @graphql-typegen auto-generated
type UpdateUserMutationData = {
__typename: 'Mutation',
updateUser: { __typename: 'User' } & UserFields,
}// @graphql-typegen auto-generated
type UpdateUserMutationVariables = {
id: ID,
values: {
username?: string,
},
}
````graphql-typegen` also supports string interpolation:
```js
const userFields = `
id
username
`const userQuery = gql`
query user($id: ID!) {
user(id: $id) {
${userFields}
}
}
`const updateUserMutation = gql`
mutation updateUser($id: ID!, $values: UpdateUser!) {
updateUser(id: $id, values: $values) {
${userFields}
}
}
`
```Output:
```js
// @graphql-typegen auto-generated
type UserQueryData = {
__typename: 'Query',
user: {
__typename: 'User',
id: ID,
username: string,
},
}// @graphql-typegen auto-generated
type UserQueryVariables = {
id: ID,
}// @graphql-typegen auto-generated
type UpdateUserMutationData = {
__typename: 'Mutation',
updateUser: {
__typename: 'User',
id: ID,
username: string,
},
}// @graphql-typegen auto-generated
type UpdateUserMutationVariables = {
id: ID,
values: {
username?: string,
},
}
```## Automatically adding type annotations to `Query`, `Mutation`, `useQuery`, `useMutation`, and `useSubscription`
`graphql-typegen` will analyze all calls to these hooks and add the correct type annotations:
### Before
```js
const userQuery = gql`
query user($id: ID!) {
user(id: $id) {
id
username
}
}
`const Foo = ({ id }: { id: ID }): React.Node => {
const { data } = useQuery(userQuery, { variables: { id } })
return{JSON.stringify(data)}
}
```### After
`graphql-typegen` inserts the type parameters `useQuery`.
```js
const userQuery = gql`
query user($id: ID!) {
user(id: $id) {
id
username
}
}
`// @graphql-typegen auto-generated
type UserQueryData = {
__typename: 'Query',
user: {
__typename: 'User',
id: ID,
username: string,
},
}// @graphql-typegen auto-generated
type UserQueryVariables = {
id: ID,
}const Foo = ({id}: {id: ID}): React.Node => {
const {data} = useQuery(userQuery, {variables: {id}})
return{JSON.stringify(data)}
}
```# Configuration
## `schemaFile` / `server`
First, you need to add the following to your `package.json` to tell `graphql-typegen` how to
find your schema:```
"graphql-typegen": {
"schemaFile": "path/to/schema.graphql"
}
```Or
```
"graphql-typegen": {
"server": "http://localhost:4000/graphql"
}
```## `tagName` (default: `gql`)
Name of the template literal tag used to identify template literals containing GraphQL queries in Javascript/Typescript code
Configure this in your `package.json`:
```
"graphql-typegen": {
"tagName": "gql"
}
```## `validate` (default: `true`)
Whether to validate each GraphQL document before processing it.
Excludes `NoUnusedFragmentsRule` from validation, in case you put fragment definitions
in separate template literals.Right now this is only configurable in your `package.json`:
```
"graphql-typegen": {
"validate": false
}
```## `addTypename` (default: `true`)
Places this may be configured, in order of increasing precendence:
### `package.json`
```
"graphql-typegen": {
"addTypename": false
}
```### in the description for a type or field in your GraphQL schema
```graphql
"""
@graphql-typegen noTypename
"""
type User {
id: ID!
name: String!
}
```### in a comment in your GraphQL document
```js
const query = gql`
query user($id: Int!) {
# @graphql-typegen addTypename
user(id: $id) {
id
name
}
}
`
```## `objectType` (default: `ambiguous`)
The type of Flow object to output, one of:
- `exact`
- `inexact`
- `ambiguous`Places this may be configured, in order of increasing precendence:
### `package.json`
```
"graphql-typegen": {
"objectType": "exact"
}
```### in the description for a type or field in your GraphQL schema
```graphql
"""
@graphql-typegen exact
"""
type User {
id: ID!
name: String!
}
```### in a comment in your GraphQL document
```js
const query = gql`
query user($id: Int!) {
# @graphql-typegen exact
user(id: $id) {
id
name
}
}
`
```## `useReadOnlyTypes` (default: `false`)
Whether to use readonly object and array types.
Places this may be configured, in order of increasing precendence:
### `package.json`
```
"graphql-typegen": {
"useReadOnlyTypes": true
}
```### in the description for a type or field in your GraphQL schema
```graphql
"""
@graphql-typegen readOnly
"""
type User {
id: ID!
name: String!
}
```### in a comment in your GraphQL document
```js
const query = gql`
query user($id: Int!) {
# @graphql-typegen mutable
user(id: $id) {
id
name
}
}
`
```## `useFunctionTypeArguments` (default: `true`)
Whether to annotate `useQuery`, `useMutation` and `useSubscription` calls with type arguments,
or annotate the input variables and output data.Configure this in your `package.json`:
```
"graphql-typegen": {
"useFunctionTypeArguments": false
}
```### When `true` (default)
Adds `` to `useQuery`:
```js
const {loading, error, data} = useQuery(query, {
variables: {id}
})
```### When `false`:
Annotates this way:
```js
const { loading, error, data }: QueryRenderProps =
useQuery(query, {
variables: ({ id }: QueryVariables),
})
```## `external as `
Makes `graphql-typegen` use the given external type for a scalar.
Places this may be configured, in order of increasing precendence:
### in the description for a type or field in your GraphQL schema
```graphql
"""
@graphql-typegen external as import { type DateISOString } from './src/types/DateISOString'
"""
scalar DateTime
```### in a comment in your GraphQL document
```js
const query = gql`
query user($id: Int!) {
user(id: $id) {
id
# @graphql-typegen external as string
createdAt
}
}
`
```## `extract [as ]`
Makes `graphql-typegen` extract the given type or field's inner type into a type alias,
instead of generating an inline type.There may be funky behavior if a selection set with inline fragment spreads is extracted.
The name of the type is used if you don't specify **as **.
Places this may be configured, in order of increasing precendence:
### in the description for a type or field in your GraphQL schema
```graphql
"""
@graphql-typegen extract
"""
type User {}
```### in a comment in your GraphQL document
```js
const query = gql`
query user($id: Int!) {
# @graphql-typegen extract as User
user(id: $id) {
id
name
}
}
`
```#### Output without `extract`
```js
// @graphql-typegen auto-generated
type UserQueryData = {
__typename: 'Query',
user: ?{
id: string,
name: string,
},
}
```#### Output with `extract as User`
```js
// @graphql-typegen auto-generated
type UserQueryData = {
__typename: 'Query',
user: ?User,
}// @graphql-typegen auto-generated
type User = {
id: string,
name: string,
}
```## `ignore [data | variables]`
`ignore data` makes `graphql-typegen` not output a data type.
`ignore variables` makes `graphql-typegen` not output a variables type.
`ignore` makes `graphql-typegen` not output either a data or variables type.This may be set in a comment in your GraphQL operation:
```js
const query = gql`
# @graphql-typegen ignore variables
query user($id: Int!) {
user(id: $id) {
id
name
}
}
`
```# CLI Usage
```
jscodeshift -t path/to/graphql-typegen/graphql-typegen.js src/**/*.js
```# Node.js API
Because `jscodeshift` unfortunately requires transform functions to be sync,
`graphql-typegen` uses an `execFileSync` hack to synchronously fetch your schema
from your schema file or server.If you're calling directly from node, you can bypass this by using `graphql-typegen-async`:
```js
import graphqlTypegen from 'graphql-typegen/graphql-typegen-async'
```It has the same API as a `jscodeshift` transform, except that it returns a `Promise` instead
of a sync result. Maybe someday `jscodeshift` will support async transforms.