An open API service indexing awesome lists of open source software.

https://github.com/lynxtaa/awesome-graphql-client

GraphQL Client with file upload support for NodeJS and browser
https://github.com/lynxtaa/awesome-graphql-client

Last synced: 5 months ago
JSON representation

GraphQL Client with file upload support for NodeJS and browser

Awesome Lists containing this project

README

          



Logo





CI/CD

npm version


Codecov





Awesome GraphQL Client


GraphQL Client with file upload support for NodeJS and browser


## Features

- [GraphQL File Upload](https://github.com/jaydenseric/graphql-multipart-request-spec) support
- Works in browsers and NodeJS
- Zero dependencies
- Small size (around 2Kb gzipped)
- Full Typescript support
- Supports queries generated by [graphql-tag](https://www.npmjs.com/package/graphql-tag)
- Supports GraphQL GET requests
- Perfect for React apps in combination with [react-query](https://www.npmjs.com/package/react-query). See [Next.js example](https://github.com/lynxtaa/awesome-graphql-client/tree/master/examples/next-js)

## Install

```sh
npm install awesome-graphql-client
```

## Quick Start

### Browser

```js
import { AwesomeGraphQLClient } from 'awesome-graphql-client'

const client = new AwesomeGraphQLClient({ endpoint: '/graphql' })

// Also query can be an output from graphql-tag (see examples below)
const GetUsers = `
query getUsers {
users {
id
}
}
`

const UploadUserAvatar = `
mutation uploadUserAvatar($userId: Int!, $file: Upload!) {
updateUser(id: $userId, input: { avatar: $file }) {
id
}
}
`

client
.request(GetUsers)
.then(data =>
client.request(UploadUserAvatar, {
id: data.users[0].id,
file: document.querySelector('input#avatar').files[0],
}),
)
.then(data => console.log(data.updateUser.id))
.catch(error => console.log(error))
```

### NodeJS

```js
import { openAsBlob } from 'node:fs'
import { AwesomeGraphQLClient } from 'awesome-graphql-client'

const client = new AwesomeGraphQLClient({
endpoint: 'http://localhost:8080/graphql',
})

// Also query can be an output from graphql-tag (see examples below)
const UploadUserAvatar = `
mutation uploadUserAvatar($userId: Int!, $file: Upload!) {
updateUser(id: $userId, input: { avatar: $file }) {
id
}
}
`

const blob = await openAsBlob('./avatar.png')

client
.request(UploadUserAvatar, { file: new File([blob], 'avatar.png'), userId: 10 })
.then(data => console.log(data.updateUser.id))
.catch(error => console.log(error))
```

## Table of Contents

- API
- [AwesomeGraphQLClient](#awesomegraphqlclient)
- [GraphQLRequestError](#graphqlrequesterror)
- [gql](#approach-2-use-fake-graphql-tag)
- [isFileUpload](#custom-isfileupload-predicate)
- Examples
- [Typescript](#typescript)
- [Error Logging](#error-logging)
- [GraphQL GET Requests](#graphql-get-requests)
- [GraphQL Tag](#graphql-tag)
- [Cookies in NodeJS](#cookies-in-nodejs)
- [Custom _isFileUpload_ Predicate](#custom-isfileupload-predicate)
- [More Examples](#more-examples)

## API

## `AwesomeGraphQLClient`

**Usage**:

```js
import { AwesomeGraphQLClient } from 'awesome-graphql-client'
const client = new AwesomeGraphQLClient(config)
```

### `config` properties

- `endpoint`: _string_ - The URL to your GraphQL endpoint (required)
- `fetch`: _Function_ - Fetch polyfill
- `fetchOptions`: _object_ - Overrides for fetch options
- `FormData`: _object_ - FormData polyfill
- `formatQuery`: _function(query: any): string_ - Custom query formatter (see [example](#graphql-tag))
- `onError`: _function(error: GraphQLRequestError | Error): void_ - Provided callback will be called before throwing an error (see [example](#error-logging))
- `isFileUpload`: _function(value: unknown): boolean_ - Custom predicate function for checking if value is a file (see [example](#custom-isfileupload-predicate))

### `client` methods

- `client.setFetchOptions(fetchOptions: FetchOptions)`: Sets fetch options. See examples below
- `client.getFetchOptions()`: Returns current fetch options
- `client.setEndpoint(): string`: Sets a new GraphQL endpoint
- `client.getEndpoint(): string`: Returns current GraphQL endpoint
- `client.request(query, variables?, fetchOptions?): Promise`: Sends GraphQL Request and returns data or throws an error
- `client.requestSafe(query, variables?, fetchOptions?): Promise<{ ok: true, data, response } | { ok: false, error, partialData }>`: Sends GraphQL Request and returns object with 'ok: true', 'data' and 'response' or with 'ok: false', 'error' and 'partialData' fields. See examples below. _Notice: this function never throws_.

## `GraphQLRequestError`

### `instance` fields

- `message`: _string_ - Error message
- `query`: _string_ - GraphQL query
- `variables`: _string | undefined_ - GraphQL variables
- `response`: _Response_ - response returned from fetch
- `fieldErrors`: _GraphQLFieldError[]_ - GraphQL field errors

## Examples

## Typescript

```ts
interface getUser {
user: { id: number; login: string } | null
}
interface getUserVariables {
id: number
}

const query = `
query getUser($id: Int!) {
user {
id
login
}
}
`

const client = new AwesomeGraphQLClient({
endpoint: 'http://localhost:3000/graphql',
})

client
.request(query, { id: 10 })
.then(data => console.log(data))
.catch(error => console.log(error))

client.requestSafe(query, { id: 10 }).then(result => {
if (!result.ok) {
throw result.error
}
console.log(`Status ${result.response.status}`, `Data ${result.data.user}`)
})
```

### Typescript with TypedDocumentNode (even better!)

You can generate types from queries by using [GraphQL Code Generator](https://www.graphql-code-generator.com/) with [TypedDocumentNode plugin](https://github.com/dotansimha/graphql-typed-document-node)

```graphql
# queries.graphql
query getUser($id: Int!) {
user {
id
login
}
}
```

```ts
// index.ts
import { TypedDocumentNode } from '@graphql-typed-document-node/core'
import { AwesomeGraphQLClient } from 'awesome-graphql-client'
import { print } from 'graphql/language/printer'

import { GetCharactersDocument } from './generated'

const gqlClient = new AwesomeGraphQLClient({
endpoint: 'https://rickandmortyapi.com/graphql',
formatQuery: (query: TypedDocumentNode) => print(query),
})

// AwesomeGraphQLClient will infer all types from the passed query automagically:
gqlClient
.request(GetCharactersDocument, { name: 'Rick' })
.then(data => console.log(data))
.catch(error => console.log(error))
```

Check out full example at [examples/typed-document-node](https://github.com/lynxtaa/awesome-graphql-client/tree/master/examples/typed-document-node)

## Error Logging

```js
import { AwesomeGraphQLClient, GraphQLRequestError } from 'awesome-graphql-client'

const client = new AwesomeGraphQLClient({
endpoint: '/graphql',
onError(error) {
if (error instanceof GraphQLRequestError) {
console.error(error.message)
console.groupCollapsed('Operation:')
console.log({ query: error.query, variables: error.variables })
console.groupEnd()
} else {
console.error(error)
}
},
})
```

## GraphQL GET Requests

Internally it uses [URLSearchParams API](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams). Consider [polyfilling URL standard](https://github.com/zloirock/core-js#url-and-urlsearchparams) for this feature to work in IE

```js
client
.request(query, variables, { method: 'GET' })
.then(data => console.log(data))
.catch(err => console.log(err))
```

## GraphQL Tag

### Approach #1: Use `formatQuery`

```ts
import { AwesomeGraphQLClient } from 'awesome-graphql-client'
import { DocumentNode } from 'graphql/language/ast'
import { print } from 'graphql/language/printer'
import gql from 'graphql-tag'

const client = new AwesomeGraphQLClient({
endpoint: '/graphql',
formatQuery: (query: DocumentNode | string) =>
typeof query === 'string' ? query : print(query),
})

const query = gql`
query me {
me {
login
}
}
`

client
.request(query)
.then(data => console.log(data))
.catch(err => console.log(err))
```

### Approach #2: Use fake `graphql-tag`

Recommended approach if you're using `graphql-tag` only for syntax highlighting and static analysis such as linting and types generation. It has less computational cost and makes overall smaller bundles. GraphQL fragments are supported too.

```js
import { AwesomeGraphQLClient, gql } from 'awesome-graphql-client'

const client = new AwesomeGraphQLClient({ endpoint: '/graphql' })

const query = gql`
query me {
me {
login
}
}
`

client
.request(query)
.then(data => console.log(data))
.catch(err => console.log(err))
```

### Approach #3: Use TypedDocumentNode instead

Perfect for Typescript projects. See [example above](#typescript-with-typeddocumentnode-even-better)

## Cookies in NodeJS

```js
import { AwesomeGraphQLClient } from 'awesome-graphql-client'
import fetchCookie from 'fetch-cookie'

const client = new AwesomeGraphQLClient({
endpoint: 'http://localhost:8080/graphql',
fetch: fetchCookie(globalThis.fetch),
})
```

## Custom _isFileUpload_ Predicate

```js
import { AwesomeGraphQLClient, isFileUpload } from 'awesome-graphql-client'

const client = new AwesomeGraphQLClient({
endpoint: 'http://localhost:8080/graphql',
// By default File, Blob, Buffer, Promise and stream-like instances are considered as files.
// You can expand this behaviour by adding a custom predicate
isFileUpload: value => isFileUpload(value) || value instanceof MyCustomFile,
})
```

## More Examples

[https://github.com/lynxtaa/awesome-graphql-client/tree/master/examples](https://github.com/lynxtaa/awesome-graphql-client/tree/master/examples)