tsxgql
TypeScript icon, indicating that this package has built-in type declarations

1.0.0 • Public • Published

TypeScript x GraphQL

A utility to enhance the developer experience of writing GraphQL in TypeScript.

Installlation

npm install --save tsxgql

Usage

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
//     }
//   }
// }

Features

Currently tsxgql can convert these GraphQL features:

  • Operations
    • Query
    • Mutation
    • Subscription
  • Inputs
    • Variables
    • Parameters
  • Data structures
    • Nested object query
    • Array query
  • Scalar types
    • number
    • string
    • boolean
    • Enum
    • Constant
    • Custom type
    • Optional types, e.g.) number | undefined
  • Fragments
  • Inline Fragments

Examples

Basic Query

query getUser {
    user {
        id
        name
        isActive
    }
}
import { query, types } from 'tsxgql'

query('getUser', {
    user: {
        id: types.number,
        name: types.string,
        isActive: types.boolean,
    },
})

Without Query Name

query {
    user {
        id
        name
        isActive
    }
}
import { query, types } from 'tsxgql'

query({
    user: {
        id: types.number,
        name: types.string,
        isActive: types.boolean,
    },
})

Basic Mutation

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,
  }),
})

Nested Query

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,
                },
            },
        },
    },
})

Array Field

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,
        },
    ]),
})

Optional Field

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,
    }),
  },
}

Constant field

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'),
    },
})

Enum field

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),
    },
})

Multiple Queries

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,
    },
})

Query Alias

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,
  },
}

Standard fragments

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,
  }),
}

Inline Fragment

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,
        }),
    },
})

discriminated union pattern

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
}

Usage w/ React Native

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>
        )
    }
}

Package Sidebar

Install

npm i tsxgql

Weekly Downloads

1

Version

1.0.0

License

GPL-3.0

Unpacked Size

111 kB

Total Files

15

Last publish

Collaborators

  • rayraegah