A utility to enhance the developer experience of writing GraphQL in TypeScript.
GPL-3.0 License
A utility to enhance the developer experience of writing GraphQL in TypeScript.
npm install --save tsxgql
Defines GraphQL-like JS Object:
import { params, types } from 'tsxgql'
const getUserQuery = {
user: params(
{ id: 1 },
{
id: types.number,
name: types.string,
bankAccount: {
id: types.number,
branch: types.optional.string,
},
},
),
}
types
helper defines types in the result, and the params
helper defines the parameters.
Converts the JS Object to GraphQL (string):
import { query } from 'tsxgql'
const gqlString = query('getUser', getUserQuery)
console.log(gqlString)
// =>
// query getUser {
// user(id: 1) {
// id
// name
// bankAccount {
// id
// branch
// }
// }
// }
Executes the GraphQL:
import { executeGraphql } from 'some-graphql-request-library'
// We would like to type this!
const result: typeof getUser = await executeGraphql(gqlString)
// As we cast `result` to `typeof getUser`,
// Now, `result` type looks like this:
// interface result {
// user: {
// id: number
// name: string
// bankAccount: {
// id: number
// branch?: string
// }
// }
// }
Currently tsxgql
can convert these GraphQL features:
number
string
boolean
number | undefined
query getUser {
user {
id
name
isActive
}
}
import { query, types } from 'tsxgql'
query('getUser', {
user: {
id: types.number,
name: types.string,
isActive: types.boolean,
},
})
query {
user {
id
name
isActive
}
}
import { query, types } from 'tsxgql'
query({
user: {
id: types.number,
name: types.string,
isActive: types.boolean,
},
})
mutation updateUserMutation($input: UserInput!) {
updateUser(input: $input) {
id
name
}
}
import { mutation, params } from 'tsxgql'
mutation('updateUserMutation', params({ $input: 'UserInput!' }, {
updateUser: params({ input: '$input' }, {
id: types.number,
name: types.string,
}),
})
query getUser {
user {
id
name
parent {
id
name
grandParent {
id
name
children {
id
name
}
}
}
}
}
import { query, types } from 'tsxgql'
query('getUser', {
user: {
id: types.number,
name: types.string,
parent: {
id: types.number,
name: types.string,
grandParent: {
id: types.number,
name: types.string,
children: {
id: types.number,
name: types.string,
},
},
},
},
})
query getUsers {
users(status: 'active') {
id
name
}
}
import { params, query, types } from 'tsxgql'
query('users', {
users: params({ status: 'active' }, [
{
id: types.number,
name: types.string,
},
]),
})
import { optional, query, types } from 'tsxgql'
query('getUser', {
user: {
id: types.number,
name: types.optional.string, // <-- user.name is `string | undefined`
bankAccount: optional({ // <-- user.bankAccount is `{ id: number } | undefined`
id: types.number,
}),
},
}
query getUser {
user {
id
name
__typename # <-- Always `User`
}
}
import { query, types } from 'tsxgql'
query('getUser', {
user: {
id: types.number,
name: types.string,
__typename: types.constant('User'),
},
})
query getUser {
user {
id
name
type # <-- `Student` or `Teacher`
}
}
import { query, types } from 'tsxgql'
enum UserType {
'Student',
'Teacher',
}
query('getUser', {
user: {
id: types.number,
name: types.string,
type: types.oneOf(UserType),
},
})
query getFatherAndMother {
father {
id
name
}
mother {
id
name
}
}
import { query, types } from 'tsxgql'
query('getFatherAndMother', {
father: {
id: types.number,
name: types.string,
},
mother: {
id: types.number,
name: types.number,
},
})
via a dynamic property.
query getMaleUser {
maleUser: user {
id
name
}
}
import { alias, query, types } from 'tsxgql'
query('getMaleUser', {
[alias('maleUser', 'user')]: {
id: types.number,
name: types.string,
},
}
query {
user(id: 1) {
...userFragment
}
maleUsers: users(sex: MALE) {
...userFragment
}
}
fragment userFragment on User {
id
name
bankAccount {
...bankAccountFragment
}
}
fragment bankAccountFragment on BankAccount {
id
branch
}
import { alias, fragment, params, query } from 'tsxgql'
const bankAccountFragment = fragment('bankAccountFragment', 'BankAccount', {
id: types.number,
branch: types.string,
})
const userFragment = fragment('userFragment', 'User', {
id: types.number,
name: types.string,
bankAccount: {
...bankAccountFragment,
},
})
query({
user: params({ id: 1 }, {
...userFragment,
}),
[alias('maleUsers', 'users')]: params({ sex: 'MALE' }, {
...userFragment,
}),
}
query getHeroForEpisode {
hero {
id
... on Droid {
primaryFunction
}
... on Human {
height
}
}
}
import { on, query, types } from 'tsxgql'
query('getHeroForEpisode', {
hero: {
id: types.number,
...on('Droid', {
primaryFunction: types.string,
}),
...on('Human', {
height: types.number,
}),
},
})
query getHeroForEpisode {
hero {
id
... on Droid {
kind
primaryFunction
}
... on Human {
kind
height
}
}
}
import { onUnion, query, types } from 'tsxgql'
query('getHeroForEpisode', {
hero: {
id: types.number,
...onUnion({
Droid: {
kind: types.constant('Droid'),
primaryFunction: types.string,
},
Human: {
kind: types.constant('Human'),
height: types.number,
},
}),
},
})
returns a type of A | B
const droidOrHuman = queryResult.hero
if (droidOrHuman.kind === 'Droid') {
const droid = droidOrHuman
// ... handle droid
} else if (droidOrHument.kind === 'Human') {
const human = droidOrHuman
// ... handle human
}
import 'babel-polyfill' // polyfill `Symbol` and `Map`
import * as React from 'react'
import { View, Text } from 'react-native'
import { query, types } from 'tsxgql'
const queryString = query({
getUser: {
user: {
id: types.number,
},
},
})
export class App extends React.Component<{}> {
render() {
return (
<View>
<Text>{queryString}</Text>
</View>
)
}
}