awesome-graphql-client
v2.1.0
Published
GraphQL Client with file upload support for NodeJS and browser
Downloads
25,339
Maintainers
Readme
Features
- GraphQL File Upload support
- Works in browsers and NodeJS
- Zero dependencies
- Small size (around 2Kb gzipped)
- Full Typescript support
- Supports queries generated by graphql-tag
- Supports GraphQL GET requests
- Perfect for React apps in combination with react-query. See Next.js example
Install
npm install awesome-graphql-client
Quick Start
Browser
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
NodeJS 20
const { openAsBlob } = require('node:fs')
const { AwesomeGraphQLClient } = require('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))
NodeJS 18
const { createReadStream, statSync } = require('node:fs')
const path = require('node:path')
const { Readable } = require('node:stream')
const { AwesomeGraphQLClient } = require('awesome-graphql-client')
class StreamableFile extends Blob {
constructor(filePath) {
const { mtime, size } = statSync(filePath)
super([])
this.name = path.parse(filePath).base
this.lastModified = mtime.getTime()
this.#filePath = filePath
Object.defineProperty(this, 'size', {
value: size,
writable: false,
})
Object.defineProperty(this, Symbol.toStringTag, {
value: 'File',
writable: false,
})
}
stream() {
return Readable.toWeb(createReadStream(this.#filePath))
}
}
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
}
}
`
client
.request(UploadUserAvatar, { file: new StreamableFile('./avatar.png'), userId: 10 })
.then(data => console.log(data.updateUser.id))
.catch(error => console.log(error))
Table of Contents
- API
- Examples
API
AwesomeGraphQLClient
Usage:
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 polyfillfetchOptions
: object - Overrides for fetch optionsFormData
: object - FormData polyfillformatQuery
: function(query: any): string - Custom query formatter (see example)onError
: function(error: GraphQLRequestError | Error): void - Provided callback will be called before throwing an error (see example)isFileUpload
: function(value: unknown): boolean - Custom predicate function for checking if value is a file (see example)
client
methods
client.setFetchOptions(fetchOptions: FetchOptions)
: Sets fetch options. See examples belowclient.getFetchOptions()
: Returns current fetch optionsclient.setEndpoint(): string
: Sets a new GraphQL endpointclient.getEndpoint(): string
: Returns current GraphQL endpointclient.request(query, variables?, fetchOptions?): Promise<data>
: Sends GraphQL Request and returns data or throws an errorclient.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 messagequery
: string - GraphQL queryvariables
: string | undefined - GraphQL variablesresponse
: Response - response returned from fetchfieldErrors
: GraphQLFieldError[] - GraphQL field errors
Examples
Typescript
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<getUser, getUserVariables>(query, { id: 10 })
.then(data => console.log(data))
.catch(error => console.log(error))
client.requestSafe<getUser, getUserVariables>(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 with TypedDocumentNode plugin
# queries.graphql
query getUser($id: Int!) {
user {
id
login
}
}
// 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
Error Logging
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. Consider polyfilling URL standard for this feature to work in IE
client
.request(query, variables, { method: 'GET' })
.then(data => console.log(data))
.catch(err => console.log(err))
GraphQL Tag
Approach #1: Use formatQuery
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.
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
Cookies in NodeJS
const { AwesomeGraphQLClient } = require('awesome-graphql-client')
const fetchCookie = require('fetch-cookie')
const client = new AwesomeGraphQLClient({
endpoint: 'http://localhost:8080/graphql',
fetch: fetchCookie(globalThis.fetch),
})
Custom isFileUpload Predicate
const { AwesomeGraphQLClient, isFileUpload } = require('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