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
- Host: GitHub
- URL: https://github.com/lynxtaa/awesome-graphql-client
- Owner: lynxtaa
- License: mit
- Created: 2020-07-12T14:36:32.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2024-04-20T15:50:45.000Z (over 1 year ago)
- Last Synced: 2024-05-19T18:55:41.285Z (over 1 year ago)
- Language: TypeScript
- Homepage: https://npm.im/awesome-graphql-client
- Size: 2.64 MB
- Stars: 49
- Watchers: 4
- Forks: 6
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
- awesome-list - awesome-graphql-client
README
## 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)