graffle-js / graffle

Simple GraphQL Client for JavaScript. Minimal. Extensible. Type Safe. Runs everywhere.
http://graffle.js.org/
MIT License
5.84k stars 308 forks source link

Make raw great #1164

Closed jasonkuhrt closed 1 week ago

jasonkuhrt commented 2 weeks ago

Perceived Problem

Ideas / Proposed Solution(s)

Summary benefits of following idea(s) over current raw approach:

  1. String and DocumentNode are two separate APIs. Consolidating them is easier to learn and use if done right.
  2. Choice between string and document node always left to user. But internally, Graffle could make better automatic decisions. For example if there are schema-driven features present, then there would be no need to parse the string into a DocumentNode. Don't require users think about this to get best performance for their chosen feature set.
  3. Separating variables and operation name from the document parameter allows us to leverage a template literal approach which is more succinct to read and it brings some other benefits, see following points.
  4. Putting gql method on chain permits IDE tooling support without having to deal with importing gql from graffle or any other helper package/module. So easier to use.
  5. The type inference is a PITA using the current object approach. I've struggled to get the variables key to depend on the state of the passed in typed document node/string/query. Moving the variables and operation name to another method makes the typing much easier to pull off. Functionally, I have been unable to get the result set to infer with types computed (aka. simplified). While correct, they are unreadable.
  6. The idea of extending gql method to root type namespaces on the chaining api seems to open up a really nice convenience method. The template literal allows us to have arguments passed in-context. It doesn't allow one operation argument to apply in two places but that's ok, its just for simple cases. Its sugar. Nice sugar. Without the template literal succinctness this idea wouldn't be compelling.

Document Level template literal with variables

const data = await graffle
  .gql`
    query pokemonByName ($Name: String!) {
      pokemonByName (name: $Name) {
        name
        continent {
          name
        }
      }
    }
  `
  .run({
    name: `Pikachu`,
  })

Document Level template literal with variables, operation name

const data = await graffle
  .gql`
    query pokemonByName ($Name: String!) {
      pokemonByName (name: $Name) {
        name
        continent {
          name
        }
      }
    }
  `
  .run(`pokemonByName`, {
    name: `Pikachu`,
  })

Passing in a type variable to have the arguments typed and results inferred.

const data = await graffle
  .gql<QueryPokemonByName>`
    query pokemonByName ($Name: String!) {
      pokemonByName (name: $Name) {
        name
        continent {
          name
        }
      }
    }
  `
  .run({
    name: `Pikachu`,
  })

type QueryPokemonByName = TypedQueryDocumentNode<
  {
    pokemonByName: {
      id: string
      name: string
      hp: number
      attack: number
      defense: number
      trainer: { name: string }
    }
  },
  { name: string }
>

Using a prepared DocumentNode instance.

The gql function can continue to be used as a way to create documents.

const document = gql`
  query pokemonByName ($Name: String!) {
    pokemonByName (name: $Name) {
      name
      continent {
        name
      }
    }
  }
`

const data = await graffle
  .gql(document)
  .run({ name: `Pikachu` })

Access the constructed DocumentNode instance

const document = graffle.gql`
  query pokemonByName ($Name: String!) {
    pokemonByName (name: $Name) {
      name
      continent {
        name
      }
    }
  }
`.document

Sugar: Root Type Level template literal. Interpolates variables into GraphQL document

const data = await graffle.query.gql`
  pokemonByName (name: ${`Pikachu`}) {
    name
    continent {
      name
    }
  }
`
P4sca1 commented 2 weeks ago

Is it possible to pass in TypedDocumentNode from https://github.com/dotansimha/graphql-typed-document-node? Im thinking about using the raw client with generated typed document nodes to avoid the client bundle overhead from the generated graffle client. Typed document nodes could likely be tree-shaken per page so that each page only bundles the graphql documents needed instead of a full-blown client.

jasonkuhrt commented 1 week ago

@P4sca1 yup that will be supported. The PR already achieved that. I just have to tidy everything up. Won't be long, today or tomorrow.