Giter Club home page Giter Club logo

graphql-over-http's Introduction

GraphQL

GraphQL Logo

The GraphQL specification is edited in the markdown files found in /spec the latest release of which is published at https://graphql.github.io/graphql-spec/.

The latest draft specification can be found at https://graphql.github.io/graphql-spec/draft/ which tracks the latest commit to the main branch in this repository.

Previous releases of the GraphQL specification can be found at permalinks that match their release tag. For example, https://graphql.github.io/graphql-spec/October2016/. If you are linking directly to the GraphQL specification, it's best to link to a tagged permalink for the particular referenced version.

Overview

This is a Working Draft of the Specification for GraphQL, a query language for APIs created by Facebook.

The target audience for this specification is not the client developer, but those who have, or are actively interested in, building their own GraphQL implementations and tools.

In order to be broadly adopted, GraphQL will have to target a wide variety of backend environments, frameworks, and languages, which will necessitate a collaborative effort across projects and organizations. This specification serves as a point of coordination for this effort.

Looking for help? Find resources from the community.

Getting Started

GraphQL consists of a type system, query language and execution semantics, static validation, and type introspection, each outlined below. To guide you through each of these components, we've written an example designed to illustrate the various pieces of GraphQL.

This example is not comprehensive, but it is designed to quickly introduce the core concepts of GraphQL, to provide some context before diving into the more detailed specification or the GraphQL.js reference implementation.

The premise of the example is that we want to use GraphQL to query for information about characters and locations in the original Star Wars trilogy.

Type System

At the heart of any GraphQL implementation is a description of what types of objects it can return, described in a GraphQL type system and returned in the GraphQL Schema.

For our Star Wars example, the starWarsSchema.ts file in GraphQL.js defines this type system.

The most basic type in the system will be Human, representing characters like Luke, Leia, and Han. All humans in our type system will have a name, so we define the Human type to have a field called "name". This returns a String, and we know that it is not null (since all Humans have a name), so we will define the "name" field to be a non-nullable String. Using a shorthand notation that we will use throughout the spec and documentation, we would describe the human type as:

type Human {
  name: String
}

This shorthand is convenient for describing the basic shape of a type system; the JavaScript implementation is more full-featured, and allows types and fields to be documented. It also sets up the mapping between the type system and the underlying data; for a test case in GraphQL.js, the underlying data is a set of JavaScript objects, but in most cases the backing data will be accessed through some service, and this type system layer will be responsible for mapping from types and fields to that service.

A common pattern in many APIs, and indeed in GraphQL is to give objects an ID that can be used to refetch the object. So let's add that to our Human type. We'll also add a string for their home planet.

type Human {
  id: String
  name: String
  homePlanet: String
}

Since we're talking about the Star Wars trilogy, it would be useful to describe the episodes in which each character appears. To do so, we'll first define an enum, which lists the three episodes in the trilogy:

enum Episode {
  NEWHOPE
  EMPIRE
  JEDI
}

Now we want to add a field to Human describing what episodes they were in. This will return a list of Episodes:

type Human {
  id: String
  name: String
  appearsIn: [Episode]
  homePlanet: String
}

Now, let's introduce another type, Droid:

type Droid {
  id: String
  name: String
  appearsIn: [Episode]
  primaryFunction: String
}

Now we have two types! Let's add a way of going between them: humans and droids both have friends. But humans can be friends with both humans and droids. How do we refer to either a human or a droid?

If we look, we note that there's common functionality between humans and droids; they both have IDs, names, and episodes in which they appear. So we'll add an interface, Character, and make both Human and Droid implement it. Once we have that, we can add the friends field, that returns a list of Characters.

Our type system so far is:

enum Episode {
  NEWHOPE
  EMPIRE
  JEDI
}

interface Character {
  id: String
  name: String
  friends: [Character]
  appearsIn: [Episode]
}

type Human implements Character {
  id: String
  name: String
  friends: [Character]
  appearsIn: [Episode]
  homePlanet: String
}

type Droid implements Character {
  id: String
  name: String
  friends: [Character]
  appearsIn: [Episode]
  primaryFunction: String
}

One question we might ask, though, is whether any of those fields can return null. By default, null is a permitted value for any type in GraphQL, since fetching data to fulfill a GraphQL query often requires talking to different services that may or may not be available. However, if the type system can guarantee that a type is never null, then we can mark it as Non Null in the type system. We indicate that in our shorthand by adding an "!" after the type. We can update our type system to note that the id is never null.

Note that while in our current implementation, we can guarantee that more fields are non-null (since our current implementation has hard-coded data), we didn't mark them as non-null. One can imagine we would eventually replace our hardcoded data with a backend service, which might not be perfectly reliable; by leaving these fields as nullable, we allow ourselves the flexibility to eventually return null to indicate a backend error, while also telling the client that the error occurred.

enum Episode {
  NEWHOPE
  EMPIRE
  JEDI
}

interface Character {
  id: String!
  name: String
  friends: [Character]
  appearsIn: [Episode]
}

type Human implements Character {
  id: String!
  name: String
  friends: [Character]
  appearsIn: [Episode]
  homePlanet: String
}

type Droid implements Character {
  id: String!
  name: String
  friends: [Character]
  appearsIn: [Episode]
  primaryFunction: String
}

We're missing one last piece: an entry point into the type system.

When we define a schema, we define an object type that is the basis for all query operations. The name of this type is Query by convention, and it describes our public, top-level API. Our Query type for this example will look like this:

type Query {
  hero(episode: Episode): Character
  human(id: String!): Human
  droid(id: String!): Droid
}

In this example, there are three top-level operations that can be done on our schema:

  • hero returns the Character who is the hero of the Star Wars trilogy; it takes an optional argument that allows us to fetch the hero of a specific episode instead.
  • human accepts a non-null string as a query argument, a human's ID, and returns the human with that ID.
  • droid does the same for droids.

These fields demonstrate another feature of the type system, the ability for a field to specify arguments that configure their behavior.

When we package the whole type system together, defining the Query type above as our entry point for queries, this creates a GraphQL Schema.

This example just scratched the surface of the type system. The specification goes into more detail about this topic in the "Type System" section, and the type directory in GraphQL.js contains code implementing a specification-compliant GraphQL type system.

Query Syntax

GraphQL queries declaratively describe what data the issuer wishes to fetch from whoever is fulfilling the GraphQL query.

For our Star Wars example, the starWarsQueryTests.js file in the GraphQL.js repository contains a number of queries and responses. That file is a test file that uses the schema discussed above and a set of sample data, located in starWarsData.js. This test file can be run to exercise the reference implementation.

An example query on the above schema would be:

query HeroNameQuery {
  hero {
    name
  }
}

The initial line, query HeroNameQuery, defines a query with the operation name HeroNameQuery that starts with the schema's root query type; in this case, Query. As defined above, Query has a hero field that returns a Character, so we'll query for that. Character then has a name field that returns a String, so we query for that, completing our query. The result of this query would then be:

{
  "hero": {
    "name": "R2-D2"
  }
}

Specifying the query keyword and an operation name is only required when a GraphQL document defines multiple operations. We therefore could have written the previous query with the query shorthand:

{
  hero {
    name
  }
}

Assuming that the backing data for the GraphQL server identified R2-D2 as the hero. The response continues to vary based on the request; if we asked for R2-D2's ID and friends with this query:

query HeroNameAndFriendsQuery {
  hero {
    id
    name
    friends {
      id
      name
    }
  }
}

then we'll get back a response like this:

{
  "hero": {
    "id": "2001",
    "name": "R2-D2",
    "friends": [
      {
        "id": "1000",
        "name": "Luke Skywalker"
      },
      {
        "id": "1002",
        "name": "Han Solo"
      },
      {
        "id": "1003",
        "name": "Leia Organa"
      }
    ]
  }
}

One of the key aspects of GraphQL is its ability to nest queries. In the above query, we asked for R2-D2's friends, but we can ask for more information about each of those objects. So let's construct a query that asks for R2-D2's friends, gets their name and episode appearances, then asks for each of their friends.

query NestedQuery {
  hero {
    name
    friends {
      name
      appearsIn
      friends {
        name
      }
    }
  }
}

which will give us the nested response

{
  "hero": {
    "name": "R2-D2",
    "friends": [
      {
        "name": "Luke Skywalker",
        "appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"],
        "friends": [
          { "name": "Han Solo" },
          { "name": "Leia Organa" },
          { "name": "C-3PO" },
          { "name": "R2-D2" }
        ]
      },
      {
        "name": "Han Solo",
        "appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"],
        "friends": [
          { "name": "Luke Skywalker" },
          { "name": "Leia Organa" },
          { "name": "R2-D2" }
        ]
      },
      {
        "name": "Leia Organa",
        "appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"],
        "friends": [
          { "name": "Luke Skywalker" },
          { "name": "Han Solo" },
          { "name": "C-3PO" },
          { "name": "R2-D2" }
        ]
      }
    ]
  }
}

The Query type above defined a way to fetch a human given their ID. We can use it by hard-coding the ID in the query:

query FetchLukeQuery {
  human(id: "1000") {
    name
  }
}

to get

{
  "human": {
    "name": "Luke Skywalker"
  }
}

Alternately, we could have defined the query to have a query parameter:

query FetchSomeIDQuery($someId: String!) {
  human(id: $someId) {
    name
  }
}

This query is now parameterized by $someId; to run it, we must provide that ID. If we ran it with $someId set to "1000", we would get Luke; set to "1002", we would get Han. If we passed an invalid ID here, we would get null back for the human, indicating that no such object exists.

Notice that the key in the response is the name of the field, by default. It is sometimes useful to change this key, for clarity or to avoid key collisions when fetching the same field with different arguments.

We can do that with field aliases, as demonstrated in this query:

query FetchLukeAliased {
  luke: human(id: "1000") {
    name
  }
}

We aliased the result of the human field to the key luke. Now the response is:

{
  "luke": {
    "name": "Luke Skywalker"
  }
}

Notice the key is "luke" and not "human", as it was in our previous example where we did not use the alias.

This is particularly useful if we want to use the same field twice with different arguments, as in the following query:

query FetchLukeAndLeiaAliased {
  luke: human(id: "1000") {
    name
  }
  leia: human(id: "1003") {
    name
  }
}

We aliased the result of the first human field to the key luke, and the second to leia. So the result will be:

{
  "luke": {
    "name": "Luke Skywalker"
  },
  "leia": {
    "name": "Leia Organa"
  }
}

Now imagine we wanted to ask for Luke and Leia's home planets. We could do so with this query:

query DuplicateFields {
  luke: human(id: "1000") {
    name
    homePlanet
  }
  leia: human(id: "1003") {
    name
    homePlanet
  }
}

but we can already see that this could get unwieldy, since we have to add new fields to both parts of the query. Instead, we can extract out the common fields into a fragment, and include the fragment in the query, like this:

query UseFragment {
  luke: human(id: "1000") {
    ...HumanFragment
  }
  leia: human(id: "1003") {
    ...HumanFragment
  }
}

fragment HumanFragment on Human {
  name
  homePlanet
}

Both of those queries give this result:

{
  "luke": {
    "name": "Luke Skywalker",
    "homePlanet": "Tatooine"
  },
  "leia": {
    "name": "Leia Organa",
    "homePlanet": "Alderaan"
  }
}

The UseFragment and DuplicateFields queries will both get the same result, but UseFragment is less verbose; if we wanted to add more fields, we could add it to the common fragment rather than copying it into multiple places.

We defined the type system above, so we know the type of each object in the output; the query can ask for that type using the special field __typename, defined on every object.

query CheckTypeOfR2 {
  hero {
    __typename
    name
  }
}

Since R2-D2 is a droid, this will return

{
  "hero": {
    "__typename": "Droid",
    "name": "R2-D2"
  }
}

This was particularly useful because hero was defined to return a Character, which is an interface; we might want to know what concrete type was actually returned. If we instead asked for the hero of Episode V:

query CheckTypeOfLuke {
  hero(episode: EMPIRE) {
    __typename
    name
  }
}

We would find that it was Luke, who is a Human:

{
  "hero": {
    "__typename": "Human",
    "name": "Luke Skywalker"
  }
}

As with the type system, this example just scratched the surface of the query language. The specification goes into more detail about this topic in the "Language" section, and the language directory in GraphQL.js contains code implementing a specification-compliant GraphQL query language parser and lexer.

Validation

By using the type system, it can be predetermined whether a GraphQL query is valid or not. This allows servers and clients to effectively inform developers when an invalid query has been created, without having to rely on runtime checks.

For our Star Wars example, the file starWarsValidationTests.js contains a number of demonstrations of invalid operations, and is a test file that can be run to exercise the reference implementation's validator.

To start, let's take a complex valid query. This is the NestedQuery example from the above section, but with the duplicated fields factored out into a fragment:

query NestedQueryWithFragment {
  hero {
    ...NameAndAppearances
    friends {
      ...NameAndAppearances
      friends {
        ...NameAndAppearances
      }
    }
  }
}

fragment NameAndAppearances on Character {
  name
  appearsIn
}

And this query is valid. Let's take a look at some invalid queries!

When we query for fields, we have to query for a field that exists on the given type. So as hero returns a Character, we have to query for a field on Character. That type does not have a favoriteSpaceship field, so this query:

# INVALID: favoriteSpaceship does not exist on Character
query HeroSpaceshipQuery {
  hero {
    favoriteSpaceship
  }
}

is invalid.

Whenever we query for a field and it returns something other than a scalar or an enum, we need to specify what data we want to get back from the field. Hero returns a Character, and we've been requesting fields like name and appearsIn on it; if we omit that, the query will not be valid:

# INVALID: hero is not a scalar, so fields are needed
query HeroNoFieldsQuery {
  hero
}

Similarly, if a field is a scalar, it doesn't make sense to query for additional fields on it, and doing so will make the query invalid:

# INVALID: name is a scalar, so fields are not permitted
query HeroFieldsOnScalarQuery {
  hero {
    name {
      firstCharacterOfName
    }
  }
}

Earlier, it was noted that a query can only query for fields on the type in question; when we query for hero which returns a Character, we can only query for fields that exist on Character. What happens if we want to query for R2-D2s primary function, though?

# INVALID: primaryFunction does not exist on Character
query DroidFieldOnCharacter {
  hero {
    name
    primaryFunction
  }
}

That query is invalid, because primaryFunction is not a field on Character. We want some way of indicating that we wish to fetch primaryFunction if the Character is a Droid, and to ignore that field otherwise. We can use the fragments we introduced earlier to do this. By setting up a fragment defined on Droid and including it, we ensure that we only query for primaryFunction where it is defined.

query DroidFieldInFragment {
  hero {
    name
    ...DroidFields
  }
}

fragment DroidFields on Droid {
  primaryFunction
}

This query is valid, but it's a bit verbose; named fragments were valuable above when we used them multiple times, but we're only using this one once. Instead of using a named fragment, we can use an inline fragment; this still allows us to indicate the type we are querying on, but without naming a separate fragment:

query DroidFieldInInlineFragment {
  hero {
    name
    ... on Droid {
      primaryFunction
    }
  }
}

This has just scratched the surface of the validation system; there are a number of validation rules in place to ensure that a GraphQL query is semantically meaningful. The specification goes into more detail about this topic in the "Validation" section, and the validation directory in GraphQL.js contains code implementing a specification-compliant GraphQL validator.

Introspection

It's often useful to ask a GraphQL schema for information about what queries it supports. GraphQL allows us to do so using the introspection system!

For our Star Wars example, the file starWarsIntrospectionTests.js contains a number of queries demonstrating the introspection system, and is a test file that can be run to exercise the reference implementation's introspection system.

We designed the type system, so we know what types are available, but if we didn't, we can ask GraphQL, by querying the __schema field, always available on the root type of a Query. Let's do so now, and ask what types are available.

query IntrospectionTypeQuery {
  __schema {
    types {
      name
    }
  }
}

and we get back:

{
  "__schema": {
    "types": [
      {
        "name": "Query"
      },
      {
        "name": "Character"
      },
      {
        "name": "Human"
      },
      {
        "name": "String"
      },
      {
        "name": "Episode"
      },
      {
        "name": "Droid"
      },
      {
        "name": "__Schema"
      },
      {
        "name": "__Type"
      },
      {
        "name": "__TypeKind"
      },
      {
        "name": "Boolean"
      },
      {
        "name": "__Field"
      },
      {
        "name": "__InputValue"
      },
      {
        "name": "__EnumValue"
      },
      {
        "name": "__Directive"
      }
    ]
  }
}

Wow, that's a lot of types! What are they? Let's group them:

  • Query, Character, Human, Episode, Droid - These are the ones that we defined in our type system.
  • String, Boolean - These are built-in scalars that the type system provided.
  • __Schema, __Type, __TypeKind, __Field, __InputValue, __EnumValue, __Directive - These all are preceded with a double underscore, indicating that they are part of the introspection system.

Now, let's try and figure out a good place to start exploring what queries are available. When we designed our type system, we specified what type all queries would start at; let's ask the introspection system about that!

query IntrospectionQueryTypeQuery {
  __schema {
    queryType {
      name
    }
  }
}

and we get back:

{
  "__schema": {
    "queryType": {
      "name": "Query"
    }
  }
}

And that matches what we said in the type system section, that the Query type is where we will start! Note that the naming here was just by convention; we could have named our Query type anything else, and it still would have been returned here if we had specified it as the starting type for queries. Naming it Query, though, is a useful convention.

It is often useful to examine one specific type. Let's take a look at the Droid type:

query IntrospectionDroidTypeQuery {
  __type(name: "Droid") {
    name
  }
}

and we get back:

{
  "__type": {
    "name": "Droid"
  }
}

What if we want to know more about Droid, though? For example, is it an interface or an object?

query IntrospectionDroidKindQuery {
  __type(name: "Droid") {
    name
    kind
  }
}

and we get back:

{
  "__type": {
    "name": "Droid",
    "kind": "OBJECT"
  }
}

kind returns a __TypeKind enum, one of whose values is OBJECT. If we asked about Character instead:

query IntrospectionCharacterKindQuery {
  __type(name: "Character") {
    name
    kind
  }
}

and we get back:

{
  "__type": {
    "name": "Character",
    "kind": "INTERFACE"
  }
}

We'd find that it is an interface.

It's useful for an object to know what fields are available, so let's ask the introspection system about Droid:

query IntrospectionDroidFieldsQuery {
  __type(name: "Droid") {
    name
    fields {
      name
      type {
        name
        kind
      }
    }
  }
}

and we get back:

{
  "__type": {
    "name": "Droid",
    "fields": [
      {
        "name": "id",
        "type": {
          "name": null,
          "kind": "NON_NULL"
        }
      },
      {
        "name": "name",
        "type": {
          "name": "String",
          "kind": "SCALAR"
        }
      },
      {
        "name": "friends",
        "type": {
          "name": null,
          "kind": "LIST"
        }
      },
      {
        "name": "appearsIn",
        "type": {
          "name": null,
          "kind": "LIST"
        }
      },
      {
        "name": "primaryFunction",
        "type": {
          "name": "String",
          "kind": "SCALAR"
        }
      }
    ]
  }
}

Those are our fields that we defined on Droid!

id looks a bit weird there, it has no name for the type. That's because it's a "wrapper" type of kind NON_NULL. If we queried for ofType on that field's type, we would find the String type there, telling us that this is a non-null String.

Similarly, both friends and appearsIn have no name, since they are the LIST wrapper type. We can query for ofType on those types, which will tell us what these are lists of.

query IntrospectionDroidWrappedFieldsQuery {
  __type(name: "Droid") {
    name
    fields {
      name
      type {
        name
        kind
        ofType {
          name
          kind
        }
      }
    }
  }
}

and we get back:

{
  "__type": {
    "name": "Droid",
    "fields": [
      {
        "name": "id",
        "type": {
          "name": null,
          "kind": "NON_NULL",
          "ofType": {
            "name": "String",
            "kind": "SCALAR"
          }
        }
      },
      {
        "name": "name",
        "type": {
          "name": "String",
          "kind": "SCALAR",
          "ofType": null
        }
      },
      {
        "name": "friends",
        "type": {
          "name": null,
          "kind": "LIST",
          "ofType": {
            "name": "Character",
            "kind": "INTERFACE"
          }
        }
      },
      {
        "name": "appearsIn",
        "type": {
          "name": null,
          "kind": "LIST",
          "ofType": {
            "name": "Episode",
            "kind": "ENUM"
          }
        }
      },
      {
        "name": "primaryFunction",
        "type": {
          "name": "String",
          "kind": "SCALAR",
          "ofType": null
        }
      }
    ]
  }
}

Let's end with a feature of the introspection system particularly useful for tooling; let's ask the system for documentation!

query IntrospectionDroidDescriptionQuery {
  __type(name: "Droid") {
    name
    description
  }
}

yields

{
  "__type": {
    "name": "Droid",
    "description": "A mechanical creature in the Star Wars universe."
  }
}

So we can access the documentation about the type system using introspection, and create documentation browsers, or rich IDE experiences.

This has just scratched the surface of the introspection system; we can query for enum values, what interfaces a type implements, and more. We can even introspect on the introspection system itself. The specification goes into more detail about this topic in the "Introspection" section, and the introspection file in GraphQL.js contains code implementing a specification-compliant GraphQL query introspection system.

Additional Content

This README walked through the GraphQL.js reference implementation's type system, query execution, validation, and introspection systems. There's more in both GraphQL.js and specification, including a description and implementation for executing queries, how to format a response, explaining how a type system maps to an underlying implementation, and how to format a GraphQL response, as well as the grammar for GraphQL.

Contributing to this repo

This repository is managed by EasyCLA. Project participants must sign the free (GraphQL Specification Membership agreement before making a contribution. You only need to do this one time, and it can be signed by individual contributors or their employers.

To initiate the signature process please open a PR against this repo. The EasyCLA bot will block the merge if we still need a membership agreement from you.

You can find detailed information here. If you have issues, please email [email protected].

If your company benefits from GraphQL and you would like to provide essential financial support for the systems and people that power our community, please also consider membership in the GraphQL Foundation.

graphql-over-http's People

Contributors

abernix avatar benjie avatar dependabot[bot] avatar enisdenjo avatar erikwittern avatar ghmcadams avatar glasser avatar glennblock avatar goldhand avatar hemanth avatar ivangoncharov avatar jaydenseric avatar jovidecroock avatar ladd avatar maraisr avatar michaelstaib avatar mike-marcacci avatar mmatsa avatar phillip-kruger avatar poornimanayar avatar ralfhandl avatar robrichard avatar robzhu avatar sgrove avatar shane32 avatar sjparsons avatar spawnia avatar sungam3r avatar tazsingh avatar trevor-scheer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

graphql-over-http's Issues

Research other serialization formats

Research some other serialization formats and whether they have implications for content-types / status codes. For example, the usual case that we consider is GraphQL over HTTP with JSON as the serialization format. However, there are other serialization formats in use and this may give us insight as to what the constraints need to be support those in addition to JSON in GraphQL over HTTP.

Cancelled: July working group meeting

Hello folks, I'm cancelling the July working group meeting today since I hadn't had a chance to work through the responses and communicate a new time. We will meet again on last Tuesday of August; later today I'll work through and communicate the responses about times. Apologies for the late notice.

@abernix @balshor @benjie @deinok @ErikWittern @jaydenseric @michaelstaib @mike-marcacci @mmatsa @sjparsons @spawnia @sungam3r @enisdenjo

Hat-tip @benjie for keeping me honest.

"Persisted queries" / "stored operations"

The terms "persisted queries" and "stored operations" are typically used interchangably. The traditional name is "persisted queries", but the term "stored operations" acknowledges that this approach can be used with mutation and subscription operations in addition to queries.

There's lots of different implementations of this feature, often with different aims. Sometimes it's used to block all but a small allow-list of operations. Sometimes it's used to reduce network bandwidth for GraphQL requests. Sometimes it's used to improve HTTP cacheability (GET requests with short URLs).

Relevant links:

Some implementations require queries to be persisted to the server in advance of receiving queries (e.g. at build time); others negotiate the storage of the query with the server for bandwidth optimisation (e.g. automatic persisted queries).

Typically persisted queries replace the query in {query, variables, operationName} with another field (typically id) that indicates an identifier for the operation - perhaps a number or hash.

Typically stored operations remove the query in {query, variables, operationName} and instead indicate the operation via a hash or similar identifier in the extensions property.

One of the optimisations that stored operations enable is that the parse and validate steps of GraphQL execution can be performed once per stored operation and cached; future requests for the same stored operation can jump straight to the execute step.

Tech stack and operational aspects for the test suite

As discussed in the last working group meeting, there is strong interest in providing a conformance test suite to go along with the spec.

Fundamentals

In order to not limit what kind of tests we can run, i narrowed down the choice to testing frameworks that allow running actual code. Some classes of tests are really hard/impossible to implement using only static request/response pairs.

JavaScript TypeScript seems like the obvious choice for the language. It is basically the lingua franca of the web and the most popular GraphQL implementations, including the reference implementation, are written in it.

Organization

To organize the tests, we might just use a test runner such as Jest or Mocha, if we want to stick with pure JavaScript. I think that would get us up and running very quickly.

Another route we could go would be to define the tests more abstractly, using a BDD tool such as https://gauge.org/ or http://cucumber.io/. That would allow us to define the test scenarios in more readable plain text and hook up the executable tests under the hood.

Assertions

For making the actual assertions, https://github.com/visionmedia/supertest seems like a popular and practical choice. Haven't really found much more in that regard, open to suggestions.

Sub-Spec Limitations?

I think we need to answer the question: Does GraphQL over HTTP describe:

  1. An HTTP server with the singular purpose of handling GraphQL requests
  2. The GraphQL endpoint of an HTTP server which may or may not handle other types of requests
  3. The GraphQL requests and responses which are part of an HTTP server that handles GraphQL requests, but may also handle other types of requests (which GraphQL may or not be part of)

For number 3:

  • how do we distinguish between GraphQL and non-GraphQL requests?
    • endpoint path?
    • content-type (for GET and POST requests?)
    • both?
  • Are GraphQL responses allowed to be combined with or transformed into something that is no longer governed by this sub-spec?
  • What kind of preprocessing is allowed by the server for GraphQL requests?

[Incremental Delivery Questions] How is the response protocol determined in different scenarios

I have a question regarding how the response protocol is determined for a request that could POTENTIALLY result in multiple payloads.

Scenario: client sends header accept: multipart/mixed with non-"Incremental Delivery" operation

Given we have an operation that does not use defer/stream at all:

query { foo }

Should the response then use the application/(graphql+)json response type or the multipart/mixed response type?

Scenario: clients sends accept: multipart/mixed, application/(graphql+)json with non-"Incremental Delivery" operation

Given we have an operation that does not use defer/stream at all:

query { foo }

Should the response then still use the application/(graphql+)json content-type or the multipart/mixed response type?
According to the current spec draft, the server should always please the order of the accept header listed content-types if possible. Would that still apply if the server is capable of serving both content-types (accept: multipart/mixed and application/(graphql+)json)

Scenario: clients sends header accept: application/(graphql+)json with "Incremental Delivery" operation

query {
  foo
  ... on Query @defer {
    bar
  }
}

Quote from current Spec Draft:

According to the HTTP 1.1 Accept specification, when a client does not include at least one supported content type in the Accept HTTP header, the server MAY choose to respond in one of several ways. The server MUST either:

Disregard the Accept header and respond with the default content type of application/graphql+json, specifying this in the Content-Type header; OR
Respond with a 406 Not Acceptable status code

Should the server in this scenario still send back multipart/mixed or raise a 406 HTTP error?

Should the fact that the client sent an operation with a defer or stream directive be a hint to the server that the client MUST be able to parse it? A client not supporting it, would probably not have requested it in the first place?

Should the server be responsible for merging the results and sending them as a single result to the client?

Should the server do the execution with the defer and stream executor functionality being disabled?

Scenario: clients sends the HTTP GET method

Is incremental delivery via GET allowed or intended?

25 August 2020 - working group meeting

Hello, just opening a discussion to remind interested devs about our working group meeting on Tuesday, August 25th. Please see the agenda and open a PR to add yourself. Note we are meeting at the time we've met at before, 1900 UTC. Feel free to add a topic if you'd like to discuss timing again.

https://github.com/APIs-guru/graphql-over-http/blob/master/working-group/agendas/2020-08-25.md

@abernix @balshor @benjie @deinok @ErikWittern @jaydenseric @michaelstaib @mike-marcacci @mmatsa @sjparsons @spawnia @sungam3r @enisdenjo @IvanGoncharov

Actions from 2020-05-26 meeting

Based on the 2020-05-26 working group meeting, here's the planned actions we'd like to work on next. If you're tagged here and you feel you cannot meet that commitment, please comment or DM me. Also, if you'd like to get involved on any of these actions just post.

(grouped by action owner)

  • @IvanGoncharov - read #79 and comment

  • @jaydenseric - add operationName tests

  • @jaydenseric Continue implementing some of the automated tests in: https://github.com/jaydenseric/graphql-http-test

  • @jaydenseric (when/if ready this month) open an issue on express-graphql for adding the test/audit suite

  • @sjparsons Share some stats on how common application/graphql is from Braintree and other sources if possible.

    Finding: Braintree's GraphQL API requires application/json currently.

  • @sjparsons Follow up with Brian, Ivan and others from the GraphQL Foundation to get momentum on the technical transfer of the repo.

    Emailed on June 30

  • @sjparsons Research some other serialization formats and whether they have implications for content-types / status codes.

  • @sjparsons Document the agreement that we should require MIME-type, making all existing servers non-compliant. Update the intention of the 1.0 release to reflect the new approach to the spec.

  • @sjparsons Begin file to document the pre-spec state and adoption guide. Include draft structure and preamble

  • @sjparsons How can the server recognize the client sent a GraphQL Request? Clarify that part in the spec.

  • @spawnia - reflect feedback from meeting in: #94

  • @spawnia - reflect feedback from meeting in: #79

Updated: Find the best time for the GraphQL over HTTP working group

Hello, we'd like to make sure that we're meeting at the best time possible for all folks. If you're interested in participating, please fill out this doodle with the best time for you on the last Tuesday of every month (Tuesday being defined at 00:00 to 23:59 in UTC). I've picked Aug 25 (next day we'll meet) for the poll, but we'll use this time going forward for every last Tuesday of the month.

As a bonus I also included times on Wednesday, Aug 26 if by chance that seems to work better for folks.

https://doodle.com/poll/wmmx4mybrwm6cew8

Deadline: August 21, 2020 I will update our agenda shortly afterwards with the time that works for the most people (hoping there's a clear winner).

@abernix @balshor @benjie @deinok @ErikWittern @jaydenseric @michaelstaib @mike-marcacci @mmatsa @sjparsons @spawnia @sungam3r @enisdenjo

Note: there was a poll for last month, but I didn't get that turned around in time to update the meeting. Apologies that you have to do this again.

Find the best time for the GraphQL over HTTP working group

Hello, we'd like to make sure that we're meeting at the best time possible for all folks. If you're interested in participating, please fill out this doodle with the best time for you on the last Tuesday of every month (Tuesday being defined at 00:00 to 23:59 in UTC). I've picked July 28 (next day we'll meet) for the poll, but we'll use this time going forward for every last Tuesday of the month.

https://doodle.com/poll/2rwvachend53zism

@abernix @balshor @benjie @deinok @ErikWittern @jaydenseric @michaelstaib @mike-marcacci @mmatsa @sjparsons @spawnia @sungam3r @enisdenjo

Migration to GraphQL Foundation

Acceptance Criteria:

  • Official, legal adoption of the in progress GraphQL over HTTP spec into the GraphQL Foundation
  • Migration of the repo into the GraphQL Foundation Github organization

This is a major step for us to make toward the 1.0 release.

This issue will track the status of this work until it's complete.

Pre-spec state and adoption guide

We would like to have a pre-spec state and an adoption guide to be release with the 1.0 version of the spec. We'll be releasing some breaking changes and we want to make it clear what implementors will need to do to become compliant.

Tasks:

  • Begin file to document the pre-spec state and adoption guide. Include draft structure and preamble
  • ... brainstorm how to make this a reality

Actions from Mar 24 meeting

Based on the meeting today, here's the planned actions we'd like to work on next. If you're tagged here and you feel you cannot meet that commitment, please comment or DM me. Also, if you'd like to get involved on any of these actions just post.

Planned before next meeting:

  • Everyone - Review and comment on the test suite PR #75
  • @sjparsons - Respond to discussion, review and comments and merge #75
  • @sjparsons - Raise an issue to discuss if we should or should not require JSON as the serialization format; Also bring to the main GraphQL working group meeting as an agenda item
  • @spawnia - Clarify the current spec around status codes
  • @benjie - Investigate the steps to getting a custom content-type: application/graphql+json
  • @sjparsons - Provide draft one sentence mission statement; share with the GraphQL working group
  • @sjparsons - Report back the state of the project legally migrating to GraphQL foundation

See agenda for notes and more details.

Make GraphQL MIME-type required

Document the agreement that we should require MIME-type, making all existing servers non-compliant. Update the intention of the 1.0 release to reflect the new approach to the spec.

X-GraphQL-Event-Stream for being notified that the GraphQL schema has updated

Server-sent events are a perfect mechanism for informing a GraphQL client that the schema has been updated. They don't require the complexity of websockets, and they are uni-directional.

I'm proposing that when introspecting a GraphQL API, if the header X-GraphQL-Event-Stream is detected then interested clients should subscribe to the text/event-stream at that (relative or absolute) URL and when it receives the change event it should automatically re-introspect the GraphQL schema.

Not much code should be required to implement this, just something like:

// refreshSchema is a function you provide which triggers re-introspection

const streamUrl = response.headers["x-graphql-event-stream"];
if (streamUrl) {
  const endpointUrl = new URL(endpoint);
  const streamUrl = new URL(streamUrl, endpointUrl);
  if (endpointUrl.host !== streamUrl.host) {
    throw new Error(
      `Stream and endpoint hosts don't match - '${streamUrl.host}' !== '${endpointUrl.host}'`
    );
  }
  const eventSource = new EventSource(streamUrl);

  eventSource.addEventListener("change", refreshSchema, false);
  eventSource.addEventListener("open", () => { /* ... */ }, false);
  eventSource.addEventListener("error", () => { /* ... */ }, false);
}

(You may want to throttle the refreshSchema method.)

This has been baked into PostGraphile for a while; I opened an implementation PR to @skevy's GraphiQL.app a good while ago; and @imolorhe has implemented it independently in Altair. Samuel wrote more about it here: https://sirmuel.design/a-better-graphql-developer-experience-with-x-graphql-event-stream-1256aef96f24. We'll probably be implementing it in the new version of GraphiQL too, once we have the plugin system in place.

What are your thoughts?

Actions from Feb 25 meeting

Based on the meeting today, here's the planned actions we'd like to work on next. If you're tagged here and you feel you cannot meet that commitment, please comment or DM me. Also, if you'd like to get involved on any of these actions just post.

Planned before next meeting:

  • @sjparsons - List of tests that we should implement
  • @benjie - Raise an issue or PR to add a User-Agent header to the client running the tests
  • @jaydenseric - rename and publish graphql-http-test
  • @IvanGoncharov - Provide one sentence mission statement
  • @IvanGoncharov - Add paragraph/sentence explaining the state of the project legally migrating to GraphQL

See agenda for notes and more details.

29 September 2020 - working group

Hello, just opening a discussion to remind interested devs about our working group meeting on Tuesday, September 29th. Please see the agenda and open a PR to add yourself. Note we are meeting at the time we've met at before, 1900 UTC. Feel free to add a topic if you'd like to discuss timing again. Unfortunately, I will not able to join this time, so @IvanGoncharov will facilitate.

https://github.com/APIs-guru/graphql-over-http/blob/master/working-group/agendas/2020-09-29.md

@abernix @balshor @benjie @deinok @ErikWittern @jaydenseric @michaelstaib @mike-marcacci @mmatsa @spawnia @sungam3r @enisdenjo @IvanGoncharov

ietf spec submission?

Should we start submitting our transport spec to ietf? And how? I've never done this before

General HTTP guidance improvements

Hi,

I see that the proposed spec is supporting most HTTP status codes. Does this group have any opinion on the following;

1-Providing guidance in the specification to use appropriate HTTP codes, particularly around the HTTP error codes 4XX/5XX to describe the outcome, without being prescriptive about which error code should be used when, would be helpful.

2-Describing how to perform Authentication Authorization for GraphQL requests over HTTP would be helpful (even though AuthN and AuthZ can be outside the scope of this spec, specifying it so and having references to standard API protocols such as OpenId connect/OAuth2 would be useful).

3-Providing some guidance/making statements around File handling (It is ok to specify that this should be done outside the GraphQL request/response handling)

4-Providing some guidance around idempotent mutation operations over HTTP

5-In the reference implementation, covering all HTTP error codes would be helpful for the Implementor (at least the most widely used codes such as 400, 401, 403, 404, 409, 422, 405, 415, 413, 500, 503

Finally coming up with directives to describe HTTP protocol specific metadata in the schema would be helpful. Some example below;

1-Declaring authorization requirements at an Operation level (@AuthPolicy or @authorization directive?)
2-Declaring that a Mutation Operation is idempotent (@idempotent directive?)
3-Declaring the HTTP request and response headers in the schema (@httpRequestHeader and @httpResponseHeader directives?)
4-Declaring the status codes of each Operations (@httpstatuscodes directive?)

Non-HTTP
1-Declare the grouping of API operations by product capability (although not HTTP specific) (@tags directive?)
2-Declaring service level objectives for an operation (@slo directive?)
3-Declaring the unique operation id for each API operation (@operationid directive?)

Happy to discuss these and contribute back. Also happy to share things we did ay PayPal around these.

Thanks,
Jayadeba

AWS AppSync HTTP behavior

@benjie @tinnou

The AppSync team is interested in participating in the HTTP specification. Here is the AppSync HTTP behavior:

AppSync supports HTTP POST and GET for queries and mutations. The URL for the endpoint is: https://.appsync-api..amazonaws.com/graphql. Query variables are sent in the HTTP POST Body, encoded as JSON or as query parameters in HTTP GET.

Errors:

Empty request body: 400 w/ Response Body:

{
  "errors": [
    {
      "message": "Request body is empty.",
      "errorType": "MalformedHttpRequestException"
    }
  ]
}

Request body containing โ€œmeowโ€ or โ€œ{}โ€ or โ€œ{}}โ€ or โ€œ[]โ€: 400 w/ Response Body:

{
  "errors": [
    {
      "message": "Unable to parse GraphQL query.",
      "errorType": "MalformedHttpRequestException"
    }
  ]
}

Request body containing โ€œ{badfield}โ€ (invalid selection): 200 w/ Response Body:

{
    "data": null,
    "errors": [
        {
            "path": null,
            "locations": [
                {
                    "line": 1,
                    "column": 2,
                    "sourceName": null
                }
            ],
            "message": "Validation error of type FieldUndefined: Field 'badfield' in type 'Query' is undefined @ 'badfield'"
        }
    ]
}

Missing query variables: 200 w/ Response Body:

{
    "data": null,
    "errors": [
        {
            "path": null,
            "locations": [
                {
                    "line": 1,
                    "column": 23,
                    "sourceName": null
                }
            ],
            "message": "Variable 'name' has coerced Null value for NonNull type 'String!'"
        }
    ]
}

Missing operation name when there are multiple GraphQL documents in the request: 400 w/ Response Body:

{
  "errors": [
    {
      "errorType": "BadRequestException",
      "message": "Missing operation name."
    }
  ]
}

Mismatched variable type: 200 w/ Response Body:

{
    "data": null,
    "errors": [
        {
            "path": null,
            "locations": [
                {
                    "line": 3,
                    "column": 11,
                    "sourceName": null
                }
            ],
            "message": "Validation error of type VariableTypeMismatch: Variable type 'Boolean!' doesn't match expected type 'String!' @ 'createEvent'"
        }
    ]
}

Missing Schema definition (API exists):

~ ยป curl --verbose --location --request POST 'https://3wawwq467zfs7fztm7zbejd2zi.appsync-api.us-west-2.amazonaws.com/graphql' \ 
--header 'x-api-key: da2-4fg5zsayc5eyzoh7yebkq74h24     ' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"query {\n    helloworld\n}","variables":{}}'

> POST /graphql HTTP/2
> Host: 3wawwq467zfs7fztm7zbejd2zi.appsync-api.us-west-2.amazonaws.com
> User-Agent: curl/7.54.0
> Accept: */*
> x-api-key: da2-4fg5zsayc5eyzoh7yebkq74h24
> Content-Type: application/json
> Content-Length: 53
>

< HTTP/2 502
< content-type: application/json;charset=UTF-8
< content-length: 117
< date: Mon, 13 Apr 2020 23:46:18 GMT
< x-amzn-requestid: 6b317af8-b592-4335-b607-d37cbf87e352
< x-amzn-errortype: GraphQLSchemaException
< x-cache: Error from cloudfront
< via: 1.1 9c60d6224ac0b44e908b5c9dcf70e9a5.cloudfront.net (CloudFront)
< x-amz-cf-pop: HIO50-C1
< x-amz-cf-id: ZYdVK8HL3O0a3miOztDcua7B9OZoxsO-updJhQJpFyG0gpszZSshwQ==
<
{
  "errors" : [ {
    "errorType" : "GraphQLSchemaException",
    "message" : "No schema definition exists."
  } ]
* Connection #0 to host 3wawwq467zfs7fztm7zbejd2zi.appsync-api.us-west-2.amazonaws.com left intact
}%

Example Mutation: (HTTP POST)

Headers:

:authority: 4oibmd5u5nahvhtdjy5zmm35oa.appsync-api.us-west-2.amazonaws.com
:method: POST
:path: /graphql
:scheme: https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: en-US,en;q=0.9
content-length: 262
content-type: application/json
origin: https://us-west-2.console.aws.amazon.com
referer: https://us-west-2.console.aws.amazon.com/appsync/home?region=us-west-2
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: cross-site
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36
x-amz-user-agent: AWS-Console-AppSync/
x-api-key: <redacted>
Payload:
{"query":"mutation CreateEvent ($name: String!) {
  createEvent(
    name: $name
    when: "Today"
    where: "My House"
    description: "Very first event"
  ) {
    id
    name
  }
}
","variables":{"name":"test"},"operationName":"CreateEvent"}

Response Headers:

access-control-allow-origin: *
access-control-expose-headers: x-amzn-RequestId,x-amzn-ErrorType,x-amz-user-agent,x-amzn-ErrorMessage,Date,x-amz-schema-version
content-length: 84
content-type: application/json;charset=UTF-8
date: Mon, 13 Apr 2020 22:57:13 GMT
status: 200
via: 1.1 b4085435efbe95a420f374958bd145bf.cloudfront.net (CloudFront)
x-amz-cf-id: e4g9Byfo3dZD9w5PxM-NCU0IkG45x0ZWL7RiHuNok-NhFFX_oMRvwQ==
x-amz-cf-pop: SEA19-C1
x-amzn-requestid: 422c227f-a63a-4b18-bf28-c375255254de
x-cache: Miss from cloudfront

Response Body:

{"data":{"createEvent":{"id":"61b34b56-e699-4f82-af2e-10a5794a1bda","name":"test"}}}

Example Query: (HTTP POST)

Headers:

:authority: 4oibmd5u5nahvhtdjy5zmm35oa.appsync-api.us-west-2.amazonaws.com
:method: POST
:path: /graphql
:scheme: https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: en-US,en;q=0.9
content-length: 153
content-type: application/json
origin: https://us-west-2.console.aws.amazon.com
referer: https://us-west-2.console.aws.amazon.com/appsync/home?region=us-west-2
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: cross-site
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36
x-amz-user-agent: AWS-Console-AppSync/
x-api-key: <redacted>
Payload:
{"query":"query ListEvents {
  listEvents {
    items {
      id
      name
    }
  }
}","variables":{"name":"test"},"operationName":"ListEvents"}

Response Headers:

access-control-allow-origin: *
access-control-expose-headers: x-amzn-RequestId,x-amzn-ErrorType,x-amz-user-agent,x-amzn-ErrorMessage,Date,x-amz-schema-version
content-length: 95
content-type: application/json;charset=UTF-8
date: Mon, 13 Apr 2020 22:53:28 GMT
status: 200
via: 1.1 b4085435efbe95a420f374958bd145bf.cloudfront.net (CloudFront)
x-amz-cf-id: mvkpGFwdSOXN200C1_CAvqP69cXrS6lnNyp6RACsj4pMX80hQV1G-w==
x-amz-cf-pop: SEA19-C1
x-amzn-requestid: 495458c1-c9af-484b-b60d-1a460c0a9605
x-cache: Miss from cloudfront

Response Body:

{"data":{"listEvents":{"items":[{"id":"61b34b56-e699-4f82-af2e-10a5794a1bda","name":"test"}]}}}

Actions from Jan 28 meeting

Based on the meeting today, here's the planned actions we'd like to work on next. If you're tagged here and you feel you cannot meet that commitment, please comment or DM me. Also, if you'd like to get involved on any of these actions just post.

Planned before next meeting

  • @sjparsons - List of tests that we should implement
  • @sjparsons - Post some of the feedback about technical requirement for the test runner on the issue #57 (test running in the browser)
  • @sjparsons - Add link to GraphQL-over-HTTP slack channel
  • @jaydenseric - Get PoC of the test runner functioning (collab with @spawnia) will look at possible integration with GraphiQL #57
  • @IvanGoncharov - Provide one sentence mission statement
  • @IvanGoncharov - Add paragraph/sentence explaining the state of the project legally migrating to GraphQL
  • @abernix - Put info about Apollo server/client in the feature matrix

Later

???? - Draft a standard sample schema - Dependent on list of things to test
Create sample servers to help test compliancy (i.e. graphql-express, graphql-api-koa, etc)


See agenda for notes and more details.

How to tell if a server implements this spec?

Tools should be able to quickly tell if a server implements the GraphQL HTTP spec or not.

Here's a rough idea of what might work for this:

Client/Tool sends an OPTIONS request to the URL that claims to be a GraphQL HTTP, Response comes with a Header like X-GRAPHQL-HTTP that includes a link to the published spec?

Setting up EasyCLA to manage GraphQL specification memberships

I'm with the Foundation and am setting up EasyCLA to manage GraphQL Specification membership signatures. Basically, as a GraphQL Project, we need to make sure all contributors have signed the (free) GraphQL Specification Membership document. The easiest way to do this is to use the EasyCLA bot, which checks each PR and verifies that the contributor has a signed spec membership on file. If they don't, it blocks the PR and provides instructions on how to get covered. Once the doc has been signed once it doesn't need to be signed again. I wrote up some detailed information here.

I've got this enabled for the main Working Group and a number of other repos. I'd like to turn this on fairly soon, and wanted to give you a heads up that it's coming. If you have questions, please let me know.

Adopt spec-md

Use https://spec-md.com/ to format the GraphQL over HTTP spec.

  • This includes taking the README.md and transforming it into the right format needed for spec-md
  • There may be a build step needed as a part of this work with Github Actions

(Edited on 7/1 with more info)

Proposal: Merge to spec more frequently

@abernix @balshor @benjie @deinok @ErikWittern @jaydenseric @michaelstaib @mike-marcacci @mmatsa @spawnia @sungam3r @enisdenjo @IvanGoncharov

Discussion with @IvanGoncharov @ghmcadams @mmatsa

Issue/Background: We want to move along discussion

  • The spec isn't evolving quickly enough - it's hard to get reviews of PRs and consensus
  • The spec isn't moving quickly enough - we want to release it while it's still relevant
  • With more PRs merging we might attract more people to participate more actively
  • The spec is short
  • It's not a spec yet, no one implements it yet, it is breaking on all servers, so it's not critical to keep master branch always clean, and it's ok to back out changes

Summary:

  • We're trying to separate "Preliminary" stage from "Draft" stage to get the spec moving more quickly until we get to Draft stage.

Proposal:

  • We already emphasize that the spec is a draft and might change dramatically. Make that stronger.
  • If a PR has no requests for changes for 2 weeks then it should be merged by one of the maintainers
  • If anyone has an objection later, they just open a PR to make the change and it goes through the same process
  • Optional: When there is lots of consensus but not 100% full consensus then:
    • We might merge the consensus-view and debate modifying it in parallel
    • Anyone can extract the non-controversial part and make a separate PR
  • Spec is now in "Preliminary" stage.
  • At some point we would promote it to "Draft" stage.
    • Before release of the spec, in "Draft" stage, we have to review the spec and review all open PRs
    • At this point every merge to master would need strong consensus
    • Only changes that address concerns
    • Implementers could start trying things

We'd like feedback from the group.
Any concerns with the Summary? Or the details?
Based on discussion here, we can open a PR.

Action from Feb 25 meeting - GraphiQL

I think @jaydenseric (?) mentioned issues integrating GraphiQL into the test suite (?). I've spoken with @acao and they would like to help with guidance/etc - could someone please expand on what the issues are/what help is needed? Feel free to move this to the relevant repository. Thanks!

Question marks are because I'm fried, and not confident I have the details right (and our live notes don't help)

"application/graphql" as official MIME type

If we release this spec in RFC format (even with "information" status) we can register application/graphql as official mime type:
https://www.iana.org/assignments/media-types/media-types.xhtml

Combining it with +json structural suffix we can use application/graphlq+json as media to for GraphQL request/responses in serialized as JSON.

Prior Art:

I don't think it should go into 1.0 release but I think it's something we should explore for post-1.0.

Define directives for HTTP compression on the client-side and HTTP caching on the server-side

I believe it would be pertinent and useful to create GraphQL directives for the client-side and server-side:

CLIENT-SIDE EXAMPLE ( @httpCompressed directive):

This directive is used to set up encoding negotiation using the Accept-Encoding: gzip HTTP request header while expecting compressed content. When the mode is set to one-off, the response needs to have the HTTP response header Content-Encoding: gzip but if the @defer or @stream directives a re used in conjunction with @httpCompressed directive, then the response needs to have the HTTP response header Transfer-Encoding: gzip, chunked and the mode will be implicitly incremental even if the mode is explicitly specified as the response will definitely be streamed using either HTTP 1.1 streaming (multipart/mixed response). The above setting tells the GraphQL server that HTTP compression should only be applied when the GraphQL response is above 3000KB.

query GetUserFriendsAndLikes ($id: Int!)  {
  getUserFriendsAndLikes (id: $id) @httpCompressed(mode: "incremental", above: 3000) {
    first_name
    last_name
    age
    gender
    friends @defer {
      first_name
      last_name
      age
      gender
    }
    likes
  }
}

SERVER-SIDE EXAMPLE (@httpCached directive)

This directive is used to activate HTTP Cache-Control header to either set them or check depending on whether the response goes through a CDN or if the resolver function output data gets requested over the network and the GraphQL server has caching capabilities built around the HTTP caching spec.

type User {
  name: String!
  age: Int!
  salary: Float!
}

type Query {
  getAllUsers (id: ID!, page: Int!, pageSize: Int!): [User!] @httpCached(action: "check", freshness: 1, validation: 0)
}

Mar 24 working group meeting

Hello, just opening a discussion to remind interested devs about our working group meeting on Mar 24th. Please see the agenda and open a PR to add yourself:

https://github.com/APIs-guru/graphql-over-http/blob/master/working-group/agendas/2020-03-24.md

@abernix @balshor @benjie @deinok @ErikWittern @jaydenseric @michaelstaib @mike-marcacci @mmatsa @sjparsons @spawnia @sungam3r @IvanGoncharov

Also, please note the scheduled time. It's the same time as usual in UTC, but some of you may live in timezones that have adjusted for daylight savings. Please note the updated time, and/or provide feedback if we should adjust this for daylight savings to accommodate folks better.

Would spec also focus on subscriptions?

Subscriptions are usually handled over the websockets but they could also utilize http protocol in situations like:

  • HTTPS connection update using SNI
  • Long polling like implementations

Would the working group consider extending graphql-over-http to include subscriptions over WebSockets ?

[Incremental Delivery RFC] Requirement on Content-Length

I had a read through the proposal and it seems like Content-Length MUST be provided for each part. I'm curious as to why this is the case - is it simply because it makes client implementation easier? From a service perspective accomplishing this behavior requires buffering the result of serialization in memory before being written to the response stream, which can have a negative impact on performance in high-throughput services. It's much more efficient to serialize directly to the response stream, and let the clients rely on the boundary markers to determine if a part is complete.

This would necessitate a more complex boundary marker, as proposed by #135.

Directives for HTTP Metadata

For GraphQL over HTTP it would be great to standardize few of the directives for describing HTTP metadata. These directives would be useful both for documentation and run time enforcement;

1-Directive for HTTP headers (request and Response) at an operation level
2-Directives for describing HTTP status codes at an operation level
3-Directive for uniquely identifying an operation ( This can be used for the purpose of linking the operation with API samples, error catalog etc.)
4-Directives for supported media types (produces and consumes) at can operation level
5- Directive for specifying security requirements at the operation level (e.g securing ana API operation using an OAuth2 scope is pretty common)

Other Directives (not related to HTTP in general but useful);

authPolicy- directive for specifying the security requirement for an API operation.
tags- For grouping API operations together (This helps wrt documentation to group similar API operations together. For example all wallet operations can be grouped under the tag "wallet-management")
slo-directive for specifying the service level objectives of an API operation (e.g. the 95th percentile response time, error rate etc.)
visibility- directive for API visibility (Public, private and partner public are pretty common categories in APIs).

Happy to discuss these and contribute back. We have many of them defined. We have many of these defined art PayPal. Happy to share them here.

Mechanism for optional features

It's come up a number of times in our discussions as a working group: We would like to have optional features that can be adopted and if adopted by a server/client then that section of the spec can be applied. I believe we need a mechanism for this.

  1. Documentation: we need to add a section to help organize and specify these optional features
  2. Communication: Servers need to communicate which features they support and clients need to communicate what features they wish to use.

I know we wish to leave many of the optional features to be defined after the 1.0 release, but I'm wondering whether the mechanism should/could be put in place for the 1.0 release, so that we have a firm foundation and clear runway after the 1.0 to start building extensions and optional features.

I'm interested in hearing (1) opinions on 1.0 or post 1.0 release and (2) ideas about possible solutions for the 2. Communication mechanism described above.

Make a list of interested developers in GraphQL over HTTP

Library developers can miss the efforts of developing a spec of GraphQL over HTTP.
It's important that in order to have interoperability over diferent programing languages or frameworks the developers of this libraries know about this, so they can explain pros and const of every decision made here.

I propose the creation of a file called INTERESTED_DEVELOPERS.md that track:

  • Programing Language
  • Frameworks (If have sense)
  • GitHub User
  • Point of View
  • Main Repository

As an example (of myself):

spaces in url query strings

In your GET example, the space in ($id: ID!) invalidates the request.

http://example.com/graphql?query=query($id: ID!){user(id:$id){name}}&variables={"id":"QVBJcy5ndXJ1"}

Do you recommend clients implementing a graphql api remove all spaces in queries or url encode the query string. For example:

url = 'http://example.com/graphql?query=' + quote('query($id: ID!){user(id:$id){name}}&variables={"id":"QVBJcy5ndXJ1"}')

Actions from 24 April 2020 meeting

Sorry, I'm a bit late in posting this... Based on the 24 April 2020 working meeting, here's the planned actions we'd like to work on next. If you're tagged here and you feel you cannot meet that commitment, please comment or DM me. Also, if you'd like to get involved on any of these actions just post.

Planned before next meeting:

  • @sjparsons Research some other serialization formats and whether they have implications for content-types / status codes.
  • @sjparsons Merge proposed automated tests #75
  • @jaydenseric (pending above) Implement some of the automated tests in: https://github.com/jaydenseric/graphql-http-test
  • @michaelstaib How many other people are using application/graphql as a common format instead of application/graphql+json for submitting requests. GitHub? Shopify? Braintree (@sjparsons can look into this) How popular is it?
  • @sjparsons How can the server recognize the client sent a GraphQL Request? Clarify that part in the spec.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.