npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

awesome-graphql-client

v2.0.0

Published

GraphQL Client with file upload support for NodeJS and browser

Downloads

17,994

Readme

Features

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 { 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

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 polyfill
  • fetchOptions: object - Overrides for fetch options
  • FormData: object - FormData polyfill
  • formatQuery: 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 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<data>: Sends GraphQL Request and returns data or throws an error
  • client.requestSafe(query, variables?, fetchOptions?): Promise<{ data, response } | { error }>: Sends GraphQL Request and returns object with 'ok: true', 'data' and 'response' or with 'ok: false' and 'error' 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

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