graphql / graphql-spec

GraphQL is a query language and execution engine tied to any backend service.
https://spec.graphql.org
14.31k stars 1.13k forks source link

Strawman: mention of non-self-describing responses #1060

Open dsogari opened 1 year ago

dsogari commented 1 year ago

I know that the spec is agnostic regarding serialization formats, but I felt the need to share this idea with future readers, since it relates to the JSON format.

Sometimes clients want to achieve the best possible data transfer rate, trading it for processing speed. In the realm of text-based protocols, one of the ways to achieve this is through compression. Another is through deduplication of entities. What I wish to propose is an additional approach to reducing the size of GraphQL responses, which I believe might be worth mentioning in the spec.

The idea is to elide all requested keys from the resulting object and rely on the structure of the GraphQL request to be able to interpret it. In the example presented below, the useful content of the expected response (ignoring whitespace) is composed of keys (46%), values (41%) and other characters (13%). By eliminating the keys, we achieve a reduction of 46% in plain text size and 26% in the gzipped version.


Consider this GraphQL query

{
  hero(episode: $episode) { # 0
    name                    # |-0
    heroFriends: friends {  # |-1
      id                    #   |-0
      name                  #   |-1
    }
  }
}

yielding the result (the data field)

{
  "hero": {
    "name": "R2-D2",
    "heroFriends": [
      {
        "id": "1000",
        "name": "Luke Skywalker"
      },
      {
        "id": "1002",
        "name": null
      },
      {
        "id": "1003",
        "name": "Leia Organa"
      }
    ]
  }
}

that can be transformed with the following jq filter

walk(if type == "object" then [.[]] else . end)

producing this equivalent (and less verbose) JSON

[
  [
    "R2-D2",
    [
      [
        "1000",
        "Luke Skywalker"
      ],
      [
        "1002",
        null
      ],
      [
        "1003",
        "Leia Organa"
      ]
    ]
  ]
]

which can be transformed back with this filter:

{
  hero: .[0] | {
    name: .[0],
    heroFriends: [.[1][] | {
      id: .[0],
      name: .[1]
    }]
  }
}

Note the similarity between this filter and the original query.