Giter Club home page Giter Club logo

graphql-js'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-js's People

Contributors

andimarek avatar asiandrummer avatar baer avatar cito avatar danielrearden avatar dherault avatar dschafer avatar enaqx avatar freiksenet avatar greenkeeper[bot] avatar greenkeeperio-bot avatar ivangoncharov avatar jeffrmoore avatar josephsavona avatar kassens avatar leebyron avatar mjmahone avatar mohawk2 avatar nodkz avatar patrickjs avatar robrichard avatar robzhu avatar saihaj avatar schrockn-zz avatar skevy avatar spawnia avatar swolchok avatar urigo avatar wincent avatar yaacovcr 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  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-js's Issues

Add tests for "context sensitivity" around on/fragment/query/mutation non-keywords

Name is context-sensitive in that, for example, the string "on" is a Name unless it appears in a place in the grammar that requires on specifically (e.g., right after an ellipsis, as in InlineFragment, or two tokens after fragment, as in FragmentDefinition). Naive application of tools like flex and bison seems likely to produce a system that doesn't allow apparent "keywords" like on/fragment/query/mutation to be used as Names (e.g., as the names of fields). We should add test cases in src/language/__tests__/parser.js that demonstrate that these are legal Names.

No way to get requested fields of the object inside `resolve`

Let's say I have a Database with User entity with a lot of fields:

var Sequelize = require('sequelize');
var sequelize = new Sequelize('sqlite://database.db');

var User = sequelize.define('User', {
  name: Sequelize.STRING,
  email: Sequelize.STRING,
  otherField: Sequelize.STRING,
});

And I have a GraphQL:

var queryType = new GraphQLObjectType({
  name: 'Query',
  fields: () => ({
    user: {
      type: userType,
      args: { id: { type: GraphQLID } },
      resolve: (_, {id}) => User.findById(id)
    },
    users: {
      type: new GraphQLList(userType),
      resolve: (source, args) => {
        User.all(); // How to get the requested fields?
        //User.all({ attributes: args._requestedFields });
      }
    },
  })
});

And I do a GraphQL query to get name of all users

 curl -s -X POST http://localhost:8080/api/graph -d '{ users { name } }' | jq '.'

I don't want to fetch all the fields from the database. I want to know what fields to fetch...

Is there any way to get a list of fields that were requested in resolve method?

Should I use GraphQL for fine-grained validation?

For example, I want to validate an email field.

Should I define my own email scalar type or just use GraphQLString and validate the value in the resolve function?

new GraphQLScalarType({
  name: 'Email',
  coerce: value => '' + value,
  coerceLiteral: ast => ast.kind === Kind.STRING && ast.value.match(/somePattern/) ?
    ast.value :
    null
});

Similar question for checking the length of a string. If I have a field that map to a VARCHAR(100) in a MySQL database, should I create a specific scalar that check for a valid length? Not sure if it's belongs to GraphQL... or if it is a good practise.

Any thoughts?

Thanks

Return types dependant on the arguments

Me and @fson at reindex.io have been working on the GraphQL implementation ourselves, ever since it was announced. Super cool stuff!

One issue I've encountered trying the reference implementaion: In our system we have fields in RootQueryType whose return type is dependent on the type argument.

query user {
  node(type: "User", id: "...") {
    // Stuff here is type checked as User
  }
  node(type: "InvalidType", id: "...") {
    // error: InvalidType is not a type
  }
  node(type: "OtherType", id: "...") {
    // Type checked as OtherType
  }
}

Is it possible to do such thing with reference implementation? I've found 'resolveType' for interfaces, but that seems to be for having ... on Type query fragments.

It is possible to just replace those generic fields (we have node, nodes, create and many more like that) with concrete fields like getUser, createUser (and that was our original plan when we started), but we feel that generic fields we ended up with are easier to use.

Thanks!

Correct way to handle unstructured property values

Hi,

First of all, I´d like to thank you for the awesome job you have done with the spec and this reference implementation. It took only couple of hours to build a GraphQL "proxy" server by generating GraphQL schema from a preexisting JSON schema definition we had for our REST API and I'm already experiencing the benefits.

The only question I have is this; We have some abstract field types in our api. For example some responses may contain user supplied JSON metadata. This means I'm unable to define the actual fields and types for the data. Currently I'm using an ugly way to handle these fields by creating a scalar type and using that for the metadata fields.

var GraphQLAnything = new GraphQLScalarType({
  name: 'Anything',
  coerce: function (value) {
    return value;
  },
  coerceLiteral: function (ast) {
    return ast.value;
  }
});

This works as desired (I'm able to receive the data in correct form) but defining something like this as a scalar feels wrong in so many levels. Especially as the spec says that "Scalars cannot have fields.". Is there a correct way to handle these type of values?

Thanks!

Allow custom directives

GraphQLSchema does not expose the use of custom directives. Based on the spec, the @skip and @include directives are required and appears to allow for custom directives.

The GraphQLSchemaConfig type doesn't allow a list of directives, and neither does the GraphQLSchema allow directives through the constructor, but it is possible to set the _directives prop on the GraphQLSchema type, so it's technically possible but not exposed.

Null and omitted attributes in InputObjects

I want to build a mutation to modify (let's be original) a ToDo object. My hypothetical ToDo object has three fields: whatToDo, notes and assignee, all of them GraphQLStrings. So I define an InputObject type with all three fields:

ToDoInput = new GraphQLInputObjectType
  name: 'ToDoInput'
  fields: -> # all of them nullable
    whatToDo: type: GraphQLString
    notes:    type: GraphQLString
    assignee: type: GraphQLString

My generic mutation will be like this:

UpdateToDoMutation = new GraphQLObjectType
  type: ToDo
  args:
    id:    type: new GraphQLNonNull GraphQLID
    attrs: type: ToDoInput
  resolve: -> # ...

Now let's assume I want to use this new mutation to do the following: "update the whatToDo field and set the assignee field to null, leaving the rest (notes) unchanged". I would love to write...

mutation M {    // [incorrect GraphQL]
  updateToDo(id: 3, attrs: {whatToDo: 'whatever', assignee: null}) { ... }
}

...that is: explicitly including assignee: null to nullify that attribute, and omitting notes to leave this attribute as it is. But unfortunately the spec (and @leebyron's Slack remark confirms it) says null values can only be modeled by leaving the attribute out, colliding with my other semantic needs: omitting an attribute to just not touch it.

Any chance the spec might support null as a valid literal value? (Otherwise: what other options are there to implement such a generic mutation?).

root as context?

This is a question for practice rather than a problem.

resolve methods take a root object as the 3rd argument, and so does the graphql method.

Is root meant as the way to pass around context information (like the session if wanting to implement viewer from the relay video examples)?

Bug in buildASTSchema when mutations are passed

All, great work all around on graphql reference implementation, it's incredibly helpful.

I have been using the schema DSL parser and buildASTSchema to materialize the schema and noticed a small bug in utilities/buildASTSchema. Specifically, when both queryTypeName and mutationTypeName are passed to the buildASTSchema, the schema is not constructed correctly - query is missing.

I traced the bug down to the lines 150-157 of utilities/buildASTSchema, where if mutationTypeName is passed, schema object is constructed with the queryTypeName string instead of queryType object:

  if (isNullish(mutationTypeName)) {
    schema = new GraphQLSchema({query: queryType});
  } else {
    schema = new GraphQLSchema({
      query: queryTypeName,
      mutation: produceTypeDef(astMap[mutationTypeName]),
    });
  }

This should fix it -

  if (isNullish(mutationTypeName)) {
    schema = new GraphQLSchema({query: queryType});
  } else {
    schema = new GraphQLSchema({
      query: queryType,
      mutation: produceTypeDef(astMap[mutationTypeName]),
    });
  }

Note that this slipped since buildASTSchema tests don't include a case when both mutation and query are passed. I am happy to fix this, add a test and submit a PR, if welcome.

Field errors

From the discussion of #44.
According to the spec on scalars under "Result Coercion":

If the server encounters some value that cannot be reasonably coerced 
to an Int, then it must raise a field error.

However it seems most examples and tests simply return null instead. What's correct?

Imported npm package throws errors within flow

Since the transformations applied before exporting the package to npm strips the flow annotations but not the comments, a lot of flow errors arise after a npm install graphql.

Here's some of them:

/Users/rricard/src/github.com/rricard/td-foundation/lib/td-mirror/node_modules/graphql/lib/error/index.js:37:25,31: parameter message
Missing annotation

/Users/rricard/src/github.com/rricard/td-foundation/lib/td-mirror/node_modules/graphql/lib/error/index.js:39:3,7: parameter nodes
Missing annotation

/Users/rricard/src/github.com/rricard/td-foundation/lib/td-mirror/node_modules/graphql/lib/error/index.js:39:10,14: parameter stack
Missing annotation

/Users/rricard/src/github.com/rricard/td-foundation/lib/td-mirror/node_modules/graphql/lib/error/index.js:73:23,27: parameter error
Missing annotation

/Users/rricard/src/github.com/rricard/td-foundation/lib/td-mirror/node_modules/graphql/lib/error/index.js:73:30,34: parameter nodes
Missing annotation

...


... 121 more errors (only 50 out of 171 errors displayed)
To see all errors, re-run Flow with --show-all-errors

One solution would be to strip the /* @flow */ comments from the npm package. But we would lose all of the useful information contained inside those annotations. If we had a transform able to put the annotations in a comment, that would be much better...

Anyway, I'm open to discuss the best way to fix this so I can start working on a pull-request and/or a new transform for flow annotations!

Including meta fields when querying the schema for an object type's fields

I expect the following query on the star wars schema

{ 
  __type(name: "Character") { 
    fields { name }
  }
}

to have the following output

{
  __type: {
    fields: [
      { name: "__typename" },
      { name: "id" },
      { name: "name" },
      { name: "friends" },
      { name: "apearsIn" }
    ]
  }
}

but the __typename field is missing because it seems to be "dynamically evaluated". Shouldn't it be part of the list of available fields?

Also, in my opinion, querying for the fields of the root query object should also include the __schema and __type fields.

Schema and type definition language parser

Are there any plans to implement the type definition shorthand language parser, so that one could actually write documents describing types instead of using the (slightly verbose) JS notation?

Broken promise behaviour when types used in list have async field resolve functions.

Okay, I'm still digging into this, and trying to simplify my examples, but it looks like something funky is going on with field resolve behaviour when promises are returned.

Here's example code which demonstrates the behaviour (as well as the results) https://gist.github.com/latentflip/f1c9520ac0d83b5237fc note how in the results, projectPromises differs from projectResults.

Okay, to explain further, the basic setup I have is:

type Org {
  id: String
  projects: [Project]
}

type Project {
  id: String
  versions: ProjectVersion
}

type ProjectVersion {
  id: String
}

and my root query just returns one org, so the query I'm executing is:

query Foo {
  org {
    id,
    projects {
      versions
    }
  }
}

which should return the org, all it's projects, and all their versions.

However, it seems if my projects.versions resolve function returns a promise, instead of immediately returning an array, something breaks. To demonstrate, in the actual code linked above, I've got a ProjectReturnType which just returns an array for it's versions and a ProjectPromiseType which returns the same array, but wrapped in a Promise.resolve(). The latter seems to break the projectPromises in the response completely, and it just returns {}.

As I understand it, a resolve function returning a value, or a resolve function returning a promise of that same value, should be exactly equivalent, right?

Spec and Build client-side GraphQL parser.

Client-side GraphQL should have access to the type IDL we refer to in the spec and any other features that are useful for client-side GraphQL development but have no semantic meaning for execution.

Type of field in resolve

When building a generic resolver (currently i'm trying to build one to map graphql to sequelize) it would be nice to be able to access information about the type of the current field.

Knowing whether or not something is a GraphQLList of a GraphQLObjectType or simply just a GraphQLObjectType will help the resolver in knowing whether to fetch an array of objects or a single object.

source and root (the first and third argument) seem to be empty, and ast (4th arg) only contains a Kind (which is 'Field') and the name value for that field.

Instrument GraphQL and expose data with built-in type

Was just brainstorming how cool it'd be if GraphQL was self-instrumented. Imagine if GraphQL logged every query + resolve timings for each field AND exposed all this data as a built-in type.

On top of this you could build:

  • Monitoring (e.g. if query times for this customer critical query goes above 100 ms, send an alert)
  • Something analogous to DBs' slow query log
  • Build tool similar to GraphiQL but for ops. Connect to your production system and watch a live log of queries. Show histogram of 20 slowest queries over the past week. Zoom in on trouble spots and prioritize speeding those queries.
  • Audit logs for users.
  • etc.

The default implementation would be in-memory but easily overridable for any serious install so logs could be persisted.

Thoughts? What does Facebook do for instrumenting/logging GraphQL?

This is potentially a huge advantage to using GraphQL. Monitoring/alerting/auditing is freaking hard and this would standardize a big chunk of it.

Adding a parser for Schema definition

This could be useful for a tool that would like to generate javascript code based on the GraphQL syntax definition.
If this sound like a good addition to the library I could draft a PR for that.

Algebraic interface types

I might be thinking about this wrong, but it feels really useful to be able to define a union of interfaces as a type. Alternatively, it would be nice to be able to supply multiple interfaces as a field type.

What I'm thinking of as a use case is a sort of type composition. Let's say I have an interface

interface Image {
  src: String
}

which represents an image I can display. Some images are stand-alone and I can link to them as a page, but other things share that capability. So I have another interface

interface Linkable {
  url: String
}

Now let's say that all users have profile pictures which are linkable. I'd like to be able to declare something like this

type User {
   profilePicture: Image + Linkable
}

When resolving such type, the only requirement is for the interfaces not to contain the same attributes. From there it should be entirely possible to find the defining interface for each attribute and then the implementing type based on interface type resolution.

Are there any plans for supporting this kind of thing or am I missing something that makes the implementation really hard or impossible?

Getting selectionSet from fieldASTs with external fragment

How can I get fields requested in external fragment from fieldASTs?

Consider following query

`
  query QueryWithFragment {
    todo(_id: "55a624bad009804e552eeea8") {
      ...TextFragment
    }
  }

  fragment TextFragment on Todo {
    text
  }
`

This query results in following, so there is no way to get those fields without directly parsing query string.

{
  "kind": "Field",
  "alias": null,
  "name": {
    "kind": "Name",
    "value": "todo",
    "loc": {
      "start": 27,
      "end": 31,
      "source": {
        "body": "\n  query UseFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ...TextFragment\n    }\n  }\n\n  fragment TextFragment on Todo {\n    text\n  }\n",
        "name": "GraphQL request"
      }
    }
  },
  "arguments": [{
    "kind": "Argument",
    "name": {
      "kind": "Name",
      "value": "_id",
      "loc": {
        "start": 32,
        "end": 35,
        "source": {
          "body": "\n  query UseFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ...TextFragment\n    }\n  }\n\n  fragment TextFragment on Todo {\n    text\n  }\n",
          "name": "GraphQL request"
        }
      }
    },
    "value": {
      "kind": "StringValue",
      "value": "55a624bad009804e552eeea8",
      "loc": {
        "start": 37,
        "end": 63,
        "source": {
          "body": "\n  query UseFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ...TextFragment\n    }\n  }\n\n  fragment TextFragment on Todo {\n    text\n  }\n",
          "name": "GraphQL request"
        }
      }
    },
    "loc": {
      "start": 32,
      "end": 63,
      "source": {
        "body": "\n  query UseFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ...TextFragment\n    }\n  }\n\n  fragment TextFragment on Todo {\n    text\n  }\n",
        "name": "GraphQL request"
      }
    }
  }],
  "directives": [],
  "selectionSet": {
    "kind": "SelectionSet",
    "selections": [{
      "kind": "FragmentSpread",
      "name": {
        "kind": "Name",
        "value": "TextFragment",
        "loc": {
          "start": 76,
          "end": 88,
          "source": {
            "body": "\n  query UseFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ...TextFragment\n    }\n  }\n\n  fragment TextFragment on Todo {\n    text\n  }\n",
            "name": "GraphQL request"
          }
        }
      },
      "directives": [],
      "loc": {
        "start": 73,
        "end": 88,
        "source": {
          "body": "\n  query UseFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ...TextFragment\n    }\n  }\n\n  fragment TextFragment on Todo {\n    text\n  }\n",
          "name": "GraphQL request"
        }
      }
    }],
    "loc": {
      "start": 65,
      "end": 94,
      "source": {
        "body": "\n  query UseFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ...TextFragment\n    }\n  }\n\n  fragment TextFragment on Todo {\n    text\n  }\n",
        "name": "GraphQL request"
      }
    }
  },
  "loc": {
    "start": 27,
    "end": 94,
    "source": {
      "body": "\n  query UseFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ...TextFragment\n    }\n  }\n\n  fragment TextFragment on Todo {\n    text\n  }\n",
      "name": "GraphQL request"
    }
  }
}

Now lets take a look in InlineFragment version

`
  query QueryWithoutFragment {
    todo(_id: "55a624bad009804e552eeea8") {
      ... on Todo {
        text
      }
    }
  }
`

We can easily access requested fields for fragment in selectionSet

{
  "kind": "Field",
  "alias": null,
  "name": {
    "kind": "Name",
    "value": "todo",
    "loc": {
      "start": 36,
      "end": 40,
      "source": {
        "body": "\n  query QueryWithoutFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ... on Todo {\n        text\n      }\n    }\n  }\n",
        "name": "GraphQL request"
      }
    }
  },
  "arguments": [{
    "kind": "Argument",
    "name": {
      "kind": "Name",
      "value": "_id",
      "loc": {
        "start": 41,
        "end": 44,
        "source": {
          "body": "\n  query QueryWithoutFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ... on Todo {\n        text\n      }\n    }\n  }\n",
          "name": "GraphQL request"
        }
      }
    },
    "value": {
      "kind": "StringValue",
      "value": "55a624bad009804e552eeea8",
      "loc": {
        "start": 46,
        "end": 72,
        "source": {
          "body": "\n  query QueryWithoutFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ... on Todo {\n        text\n      }\n    }\n  }\n",
          "name": "GraphQL request"
        }
      }
    },
    "loc": {
      "start": 41,
      "end": 72,
      "source": {
        "body": "\n  query QueryWithoutFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ... on Todo {\n        text\n      }\n    }\n  }\n",
        "name": "GraphQL request"
      }
    }
  }],
  "directives": [],
  "selectionSet": {
    "kind": "SelectionSet",
    "selections": [{
      "kind": "InlineFragment",
      "typeCondition": {
        "kind": "Name",
        "value": "Todo",
        "loc": {
          "start": 89,
          "end": 93,
          "source": {
            "body": "\n  query QueryWithoutFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ... on Todo {\n        text\n      }\n    }\n  }\n",
            "name": "GraphQL request"
          }
        }
      },
      "directives": [],
      "selectionSet": {
        "kind": "SelectionSet",
        "selections": [{
          "kind": "Field",
          "alias": null,
          "name": {
            "kind": "Name",
            "value": "text",
            "loc": {
              "start": 104,
              "end": 108,
              "source": {
                "body": "\n  query QueryWithoutFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ... on Todo {\n        text\n      }\n    }\n  }\n",
                "name": "GraphQL request"
              }
            }
          },
          "arguments": [],
          "directives": [],
          "selectionSet": null,
          "loc": {
            "start": 104,
            "end": 108,
            "source": {
              "body": "\n  query QueryWithoutFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ... on Todo {\n        text\n      }\n    }\n  }\n",
              "name": "GraphQL request"
            }
          }
        }],
        "loc": {
          "start": 94,
          "end": 116,
          "source": {
            "body": "\n  query QueryWithoutFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ... on Todo {\n        text\n      }\n    }\n  }\n",
            "name": "GraphQL request"
          }
        }
      },
      "loc": {
        "start": 82,
        "end": 116,
        "source": {
          "body": "\n  query QueryWithoutFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ... on Todo {\n        text\n      }\n    }\n  }\n",
          "name": "GraphQL request"
        }
      }
    }],
    "loc": {
      "start": 74,
      "end": 122,
      "source": {
        "body": "\n  query QueryWithoutFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ... on Todo {\n        text\n      }\n    }\n  }\n",
        "name": "GraphQL request"
      }
    }
  },
  "loc": {
    "start": 36,
    "end": 122,
    "source": {
      "body": "\n  query QueryWithoutFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ... on Todo {\n        text\n      }\n    }\n  }\n",
      "name": "GraphQL request"
    }
  }
}

I'm currently working on conversion fieldASTs to MongoDB projections. I've done with InlineFragment. Any thoughts?

GraphQL doing unnecessary/repetitious requests/resolves.

Hi,

Given the following type language schema:

type Principal {
  id: ID!
  name: String!
  partners: [Partner]
}

type Partner {
  id: ID!
  name: String!
  address: Address
  principal: Principal
}

and the following query:

{ Partners { 
  id name principal { 
    name id partners { 
      name principal { 
        name } }  } } }

we see the following requests for data on the backend:

Received request: uid: c5efdde0-7453-4346-81f0-2303a15cc5f3, entity: Partner, filter: all()
Received request: uid: 5c7ad531-baf7-49d1-9b3a-4a10033fba55, entity: Principal, filter: id=1
Received request: uid: e8df54b7-a6d3-47f2-aaa8-7331888cb888, entity: Principal, filter: id=1
Received request: uid: 555e4cea-1cbe-4893-81ce-4c94337e6061, entity: Partner, filter: id=1
Received request: uid: 388165bb-c386-4599-9a2f-85d7cdfda373, entity: Partner, filter: id=2
Received request: uid: 909fd7e9-0921-406c-89d2-881cc827208d, entity: Partner, filter: id=1
Received request: uid: 2462476d-3436-487f-9270-68208e1e5296, entity: Partner, filter: id=2
Received request: uid: bc24de58-25ee-426b-ad77-3994e3dc03ad, entity: Principal, filter: id=1
Received request: uid: 9dea18b2-bc54-4939-b0b5-8d402d8ad1a3, entity: Principal, filter: id=1
Received request: uid: 9935c61b-e386-4fd3-ad36-fb7dff5ed954, entity: Principal, filter: id=1
Received request: uid: 6edd79b0-9333-4741-8bfd-6319909e7c5d, entity: Principal, filter: id=1

[uids are generated per unique call to resolve].

These repetitious requests came as a surprise given that one of GraphQL's biggest selling points is the elimination of N+1 queries and the introduction of intelligent caching/simplification/reduction etc etc etc

Is this a known issue?

Coercing variables

Currently Scalar types have two methods coerce and coerceLiteral. coerceLiteral is used to create a javascript value from ast, coerce is used both on variables and on data received from resolve in executors.

Unfortunately that is not enough - variables usually come from some format like json which wouldn't always support all the new scalar types and most likely will return them as string. On the other hand, eg, databases might return types in javascript format already. Therefore coerce can't support both variable coercion and execution coercion.

@leebyron suggested that for variable coercion, a new method is added to the Scalars - coerceVariable.

Reducing executor

Hello!

In our system we took GraphQL query and translated it to ReQL (RethinkDB query language) for execution. This allowed us to optimize the query, eg use built-in joins in ReQL instead of querying the database two times. I can see how the similar system can be used for, eg, SQL queries. We also did some further optimizations, like returning only the fields that are requested by GraphQL, reducing the response payload size for tables with many keys.

I see a way to do it graphql-js by manually going through selection sets in resolve, but that feels very clumsy and hacky, as the library does this walking already. Some ways to do it would be to allow children resolve to modify root or to split collect/resolve/complete steps into independent parts that can be used separately.

Do you think deferred execution and query translation is a good idea? Should we stick to current model and do multiple requests to the database? Is there some solution to that, which I don't see?

Thanks!

Integer coercion is misleading

The coerce function for the integers is defined as

export var GraphQLInt = new GraphQLScalarType({
  name: 'Int',
  coerce(value) {
    var num = +value;
    return num === num && num <= MAX_INT && num >= MIN_INT ? num | 0 : null;
  },
  coerceLiteral(ast) {
    if (ast.kind === Kind.INT) {
      var num = parseInt(ast.value, 10);
      if (num <= MAX_INT && num >= MIN_INT) {
        return num;
      }
    }
  }
});

Which means that if a number does not fit the 32 bits, then the significant bits will be truncated.

Which is generally a not expected behaviour, since the coercion must either produce a valid result, or a null.

References:

Directives for the schema definition language

It is often useful to associate additional metadata with the schema. I suggest directives similar to those in GraphQL documents to be added to the schema definition language for this purpose.

The most common usecases for them are probably descriptions and deprecation reasons. But like normal GraphQL directives, they also provide an extension point for adding other metadata, such as additonal information about a connection as shown in this example.

interface Node
@doc(description: "Object with an ID")
{
  id: ID!
}

interface Connection {
  nodes: [Node]
}

type Story implements Node
@doc(description: "Story in the feed")
{
  id: ID!
  author: User
  editor: User
}

type User implements Node
@doc(description: "Story in the feed")
{
  id: ID!
  name: String
  stories: Connection @through(field: "author", ofType: "Story")
  username: String @deprecated(reason: "Use name instead")
}

type Query {
  story(id: ID!): Story
  user(id: ID!): User
}

We are experimenting with generating GraphQL.js schemas from the schema definition language and this syntax would provide a nice way add metadata to the types for that purpose.

I have implemented a proof of concept of this syntax in the schema parser, so I can also open a PR, if this is a good idea.

Processing after attribute was resolved

It would be very useful to add another callback to a field definition which would give you a chance to "post-process" the field value once it has been resolved.

This is useful for collection filtering when using interfaces. The inputs for different types implementing the interface are often different, but the outputs are the same (i.e. the interface structure). The fields on the interface are also likely to be arguments of list fields, e.g.

interface Article {
  genre: String
}

type Blog : Article {
  genre: String
}

type News : Article {
  genre: String
}

type Feed {
   items(genre: [String]): [Article]
}

If Blogs and News come in in different formats and are coerced into the same structure by their respective types, without the post-processing the filtering is tricky and needs to do the same kind of type detection Article does to decide on how to resolve the genre for filtering, which is also already being done by Blog and News as well.

Am I missing some obvious way to do this?

Suggest the ability to define resolve at the type level

I understand the point given in the examples re- parallel field resolve calls, but generally I would think that it would more common on the back end, certainly at the leaf type level, to be able to resolve all the fields necessary for a given type in one go. Could a type level resolve capability be added to the spec.

Pass rootValue from execution context to resolveType

It would be quite useful to get the rootValue in resolveType the same way you get it in field resolvers.

In my case I need to dynamically chose which fetching backend implementation to use and I'm passing it in in the rootValue. The backend also does list filtering by various predicates, one of which is the item type - the backend therefore needs to be able to resolve the item type the same way the interface does.

The natural place for this logic is the backend itself (all backend-specific things sit in one place), but since I only know the right implementation at query run time the type detection logic is now duplicated.

I can imagine other use-cases for the rootValue in resolveType - e.g. using an authorisation system to resolve to basic or extended type (guess that is kinda contrived) or some sort of user-specified type resolution rules maybe.

Suggest more languge agnostic schema definition

It would be nice to define the schema definition in a language agnostic way e.g. xml so that the schema could be shifted between graphQL implementations. This could also one to use more mature implementations in the meantime.

Bug in buildASTSchema: Adding a Float scalar to type definition breaks the materializer

When a Float field is added to type definition, the materializer (buildASTSchema) breaks down with Error: Type Float not found in document. Adding a float field to HelloScalars test confirms the issue.

Patch is simple, one-line tweak to getTypeDefProducer function in utilities/buildASTSchema.js to add Float to innerTypeMap map, as follows:

  function getTypeDefProducer() {

    var innerTypeMap = {
      String: GraphQLString,
      Int: GraphQLInt,
++++  Float: GraphQLFloat,
      Boolean: GraphQLBoolean,
      ID: GraphQLID,
    };

With this, the new HelloScalars test passes, I'll submit a PR in a sec (updated test + patch).

Is it appropriate to use Interface as marker interfaces.

My use case is to implement a generic graphql server and I would like the ability to infer relationship between types from the schema.
To achieve this, I am thinking of using Interfaces as markers to allow the server to provide additional features to decorated types.

Is a valid use of Interfaces or is it more of a hack.

Unable to make cross links between types

Vectors in graph could reference to each other in any direction.
So I should be able to use GraphQL type before it's implementation.
I don't found a possibility to do this right now. Am I right?

Using graphql client-side

Can you add a changelog with description of new features usage. This commit looks like we can now use GraphQL on the client, but there is now good description on how to do it right.

...on UnionType { fields } does not respond with the actual type of data

Spec: http://facebook.github.io/graphql/#sec-Unions

I find it quite surprising that doing ...on UnionType { fields }, the response object just contains the fields and nothing about the type of the data being returned.

If we take the example in spec further and add a new type video which also has height and width fields:

type Video {
  height: Int
  width: Int
 }

And then add it to search result union type:

 Union SearchResult = Photo | Video | Person

If we had a root query search(query: "foo") that returned a GraphQLList(SearchResult) and did something like:

 {
   search(query: "foo") {
     ... on Person {
       name
     }
     ... on Video {
      width
      height
     }
     ... on Photo {
      width
      height
     }
   }
}

The response could end up looking like this:

{
 "data": {
    "search": [
      { "name": "John Doe" }, // a Person
      { "width": 1024, "height": 768 }, // a Photo
      { "width": 2560, "height": 1440 } // a Video
    ]
  }
}

How the client is supposed to know what type the results are? I could inspect the returned fields, but that doesn't work when Photo and Video has exactly the same fields. Also it feels kind of hacky.

One solution to this is add a type field for Video, Photo and Person and require the client to request that field also. But is there really a scenario where you don't need this information?

I think what should happen by default is that the value is wrapped with the actual type of each result. For example, the above response would become something like:

{
 "data": {
    "search": [
      { "Person": { "name": "John Doe" } }, // a Person
      { "Photo": { "width": 1024, "height": 768 } }, // a Photo
      { "Video": { "width": 2560, "height": 1440 } } // a Video
    ]
  }
}

With this response, the client can just loop over the results and do something like this:

if (result.Person) { renderPersonResult(result.Person) }
else if (result.Photo) { renderPhotoResult(result.Photo) }
else if (result.Video) { renderVideoResult(result.Video) }

What do you think?

Don't swallow all errors in graphql function

There seems to be no way to get errors out of graphql function and one only gets error title, which can be the dreaded undefined is not a function deep inside schema or resolve. Would be nice if there'd be a flag to let errors pass through from graphql function, when they are not validation errors. I'd be willing to implement it if this change makes sense.

Thanks!

Promise support in the resolve() function

I started to work on this graphql implementation in order to integrate it at least partially in our systems. We would like to stay very close to this implementation as much as we can as it may become the first community-supported implementation.

A feature we would need is the ability to wait Promise results when resolving. It doesn't seem that the current implementation support it.

I would like to discuss with you if you are open to support the addition of Promise responses. In this case, I can handle the Pull-request.

So if I were to implement this, I would do it like this:

  1. Write tests with promises (either mix promises and direct calls in the current test schemas or create a completely new test schema with Promises)
  2. When resolving the query, wait for the promises when a resolve function returns a Promise. At the end, this doesn't need any API change since the result goes to a final promise when querying anyway.

We can discuss all of that if you wish of course!

Fragments don't match dynamically defined interface implementations

This is a total edge case, but here goes: In my current server implementation I need to support two backends and decide which one to use every time a run a query. So what I did was parametrise all types by the backend they are supposed to use. I made sure there is always a single instance of a type for a particular backend, but the side effect of this is the types don't all get defined before running any queries. They only get defined when resolving an interface type. The original resolveType looked like this:

new GraphQLInterfaceType({
  name: 'Content',
  ...
  resolveType: (value) => {
    return (value.item ? ContentV1(backend) : ContentV2(backend));
  },
}

Having a fragment defined like this

fragment Basic on Content {
    id
    title
    lastPublished
}

and then running a query containing

    somecontentV1 {
        items {
            ... Basic
        }
    }
    somecontentV2 {
        items {
            ... Basic
        }
    }

breaks trying to resolve the fragment on type Content. The reason is the possible type names caching in isPossibleType here:

isPossibleType(type: GraphQLObjectType): boolean {
    var possibleTypeNames = this._possibleTypeNames;
    if (!possibleTypeNames) {
      this._possibleTypeNames = possibleTypeNames =
        this.getPossibleTypes().reduce(
          (map, possibleType) => ((map[possibleType.name] = true), map),
          {}
        );
    }
    return possibleTypeNames[type.name] === true;
  }

(https://github.com/graphql/graphql-js/blob/master/src/type/definition.js#L439)

If for instance the first match for a Content interface is ContentV1, it will get created, then a fragment gets matched which calls isPossibleType and caches a single possible type name - ContentV1. When Content resolves as ContentV2, the same thing happens on fragment resolution, but this time, the cache already exists, so although ContentV2 is now defined, the fragment doesn't recognise it as a valid type name. As a result you get an empty object.

I fixed the issue by changing the resolveType to the following

resolveType: (value) => {
  const v1 = ContentV1(backend);
  const v2 = ContentV2(backend);

  return (value.item ? v1 : v2);
}

that is, instantiating both types eagerly.

I'm not sure this is a common enough case to remove the optimisation, but it should definitely be mentioned somewhere in big bold type that all interface implementations need to be defined at the same time.

Thoughts?

Making sure the input data for resolvers is consistent

This is not really as much a problem as a question about practices. Playing with GraphQL I realised that argument resolutions rely on types always getting the same data structure from the parent resolver (unless you want to branch the logic in them).

Is there any clever way to ensure this? So for instance wherever I define an attribute of the Image type, I can make sure a resolver for that attribute eventually returns a data structure which attribute resolvers on Image can handle.

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.