Skip to content
Graffle is a work in progress. Learn more.

Graffle

Simple GraphQL Client for JavaScript

Minimal. Extensible. Type Safe. Runs everywhere.

Graffle

Pre-Release Software

Graffle is still in development. Install with npm install graffle@next graphql to use the latest pre-release version.

Quick Start

ts
import { Graffle } from 'graffle'

const graffle = Graffle
  .create()
  .transport({ url: 'https://countries.trevorblades.com/graphql' })

const data = await graffle.gql`
  query {
    countries(filter: { name: { in: ["Canada", "Germany", "Japan"] } }) {
      name
      capital
      emoji
    }
  }
`.send()

console.log(data)
//          ^?

Why Choose Graffle?

Reasons to use

  • Full Type Safety — Inference for GraphQL syntax, optional generation for Document Builder
  • Flexibility — Start simple, progressively adopt type-safe features
  • Extensibility — Powerful extension system (OpenTelemetry, schema errors, custom scalars)
  • Multi-Transport — Execute against HTTP or in-memory schemas

Reasons to not use

Read the detailed comparison →

Document Builder

Optional generated TypeScript API for building type-safe queries. Get full IntelliSense and static type safety with a native TypeScript interface instead of GraphQL strings.

ts
const pokemons = await graffle.query.pokemons({
  $: { filter: { name: { in: ['Pikachu', 'Charizard'] } } },
  name: true,
  hp: true,
})

GraphQL Strings with Type Inference

Use standard GraphQL syntax with full type inference. Write queries as strings and get complete type safety for variables and results without any code generation.

ts
const data = await graffle.gql(`
  query pokemonByName($name: String!) {
    pokemonByName(name: $name) {
      name
      hp
    }
  }
`).pokemonByName({ name: 'Pikachu' })

Powerful Extensions

Compose middleware to add observability, file uploads, schema errors, and more. Build your own extensions with a clean, type-safe API.

ts
const graffle = Graffle
  .create()
  .use(OpenTelemetry())
  .use(SchemaErrors)

const data = await graffle.query.pokemons({ name: true })

Custom Scalars

Register codecs for custom scalars and Graffle automatically encodes arguments and decodes results. Works seamlessly with dates, big integers, or any custom type.

ts
const graffle = Graffle.create().scalar('Date', {
  decode: (value: string) => new Date(value),
  encode: (value: Date) => value.toISOString(),
})

const pokemons = await graffle.query.pokemons({
  $: { filter: { birthday: { lte: new Date('1987-01-13') } } },
  birthday: true, // JavaScript Date
})
Jason Kuhrt

Jason Kuhrt

Creator

Creator of Graffle, Molt, Paka, and Nexus. Former @prisma. Building tools for GraphQL and TypeScript ecosystems.

Released under the MIT License.