Giter Site home page Giter Site logo

express-graphql'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.

express-graphql's People

Contributors

acao avatar ags- avatar asiandrummer avatar baer avatar brianwarner avatar calebmer avatar chentsulin avatar danielrearden avatar dschafer avatar graingert avatar greenkeeper[bot] avatar helfer avatar heneryville avatar hzoo avatar ivangoncharov avatar jamesbehr avatar jamiehodge avatar juffalow avatar junminstorage avatar kyarik avatar lacker avatar leebyron avatar matthiaskunnen avatar mhagmajer avatar oliviertassinari avatar robrichard avatar santino avatar tuananhtd avatar wbyoung avatar wincent 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

express-graphql's Issues

Content length or transfer encoding response headers

I'm running GraphQL on a few virtual machines within Amazon's EC2 service. These virtual machines are accessed via a load balancer. The load balancer performs some health checks to ensure that the virtual machines are healthy. One of the possible health checks is an HTTP "ping" which simply makes a GET request to a specified path and for the response to be considered healthy it has to respond with a 200 code and either a content length header set, or a transfer encoding header set to chunked.

Right now, GraphQL sets neither. I'd imagine the content length would be the one that makes sense for express-graphql, since it doesn't look like it uses chunking(?).

Do you think we could set the content length header in the response? Or do is there a way to add a middleware after graphql's that calculates that and sets the header?

Building

How does one build this package if forked. I have forked it and made my changes but I get the following error when trying to import it:

Error: Cannot find module 'express-graphql'
    at Function.Module._resolveFilename (module.js:339:15)
    at Function.Module._load (module.js:290:25)
    at Module.require (module.js:367:17)
    at require (internal/module.js:16:19)
    at Object.<anonymous> (/Users/jwaldrip/dev/src/github.com/brandfolder/graphqlify/server.js:7:23)
    at Module._compile (module.js:413:34)
    at loader (/Users/jwaldrip/dev/src/github.com/brandfolder/graphqlify/node_modules/babel-register/lib/node.js:130:5)
    at Object.require.extensions.(anonymous function) [as .js] (/Users/jwaldrip/dev/src/github.com/brandfolder/graphqlify/node_modules/babel-register/lib/node.js:140:7)
    at Module.load (module.js:357:32)
    at Function.Module._load (module.js:314:12)

I tried also running npm run build but then got this error:

$ npm run build

> [email protected] build /Users/jwaldrip/dev/src/github.com/brandfolder/express-graphql
> rm -rf dist/* && babel src --ignore __tests__ --out-dir dist

SyntaxError: src/index.js: Unexpected token (19:12)
  17 | import { parseBody } from './parseBody';
  18 | import { renderGraphiQL } from './renderGraphiQL';
> 19 | import type { Request, Response } from 'express';
     |             ^
  20 |
  21 | /**
  22 |  * Used to configure the graphQLHTTP middleware by providing a schema

npm ERR! Darwin 15.4.0
npm ERR! argv "/usr/local/Cellar/node/5.5.0/bin/node" "/usr/local/bin/npm" "run" "build"
npm ERR! node v5.5.0
npm ERR! npm  v3.5.3
npm ERR! code ELIFECYCLE
npm ERR! [email protected] build: `rm -rf dist/* && babel src --ignore __tests__ --out-dir dist`
npm ERR! Exit status 1

Update GraphiQL

I'm requesting to update GraphiQL because In 0.4.5 GraphQLEnum docs use right description for values

Server Session keep refreshing

I am not sure if this is a bug.
At the GraphQL

new GraphQLObjectType({
name: 'MyType',
fields: {
myField: {
type: GraphQLString,
resolve(parentValue, _, { rootValue: { session } }) {
// use session here
}
}
}
});
I am able to see the session the ID and variables value that I set.
But when I press F5(Refresh), the session ID got refreshed, and the variables values become undefined.
My understanding for server session is, the session will keep alive as long as it is not yet expired. Also, it will be staying as long as we didn't Close the browser.
The case that I am seeing now, is even if I click a link or refresh, the sessionID is already renewed to a new ID, and the value that I previously set for the session variable become undefiend.

Is my understanding on the server session is wrong? or there is a bug on this?

Thanks.

Pass response as second parameter to function version of graphQLHTTP

I'm experimenting with the idea of being able to send non-graphql messages back to the client via custom response headers. The idea is to add the response to the rootValue so that resolve functions in mutations can access it. Then i'd do something like rootValue.response.setHeader("X-NotificationMessage", "Jolly Good!")

For this to work, the response needs to be accessible in the advanced function form of graphQLHTTP

Cannot get GraphiQL to send/display requests

I installed express-graphql, when I open 112.74.106.164:3000/graphql in the browser, graphiql is loaded. But nothing happen on graphql request. For example:

{
  authors {
    name
  }
}

Mongodb is running well, and this request work if I run directly from the server. But no chance through Graphiql...

Here is my server.js:

require('babel/register');
var express = require('express');
var graphql = require('graphql');
var expressGraphql = require('express-graphql');
var Schema = require('./server/schema.js');

var app = express();

app.use('/', expressGraphql({
  schema: Schema,
  graphiql: true
}));

app.listen(3000);
console.log('GraphQL Sandbox started on port: 3000');

And here is the schema.js:

const mongo = require('promised-mongo');
const db = mongo('mongodb://localhost/mydb');
const authorsCollection = db.collection('authors');

import * as _ from 'underscore';

import {
  GraphQLList,
  GraphQLObjectType,
  GraphQLSchema,
  GraphQLString,
  GraphQLInt,
  GraphQLFloat,
  GraphQLEnumType,
  GraphQLNonNull
} from 'graphql';

const Author = new GraphQLObjectType({
  name: 'Author',
  description: 'Represent the type of an author of a blog post or a comment',
  fields: () => ({
    _id: {type: GraphQLString},
    name: {type: GraphQLString},
    twitterHandle: {type: GraphQLString}
  })
});

const Query = new GraphQLObjectType({
  name: 'RootQuery',
  fields: {
    authors: {
      type: new GraphQLList(Author),
      resolve: function() {
        return authorsCollection.find().toArray();
      }
    }
  }
});
const Mutation = new GraphQLObjectType({
  name: 'Mutations',
  fields: {
    createAuthor: {
      type: Author,
      args: { 
        _id: {type: new GraphQLNonNull(GraphQLString)},
        name: {type: new GraphQLNonNull(GraphQLString)},
        twitterHandle: {type: GraphQLString}
      },
      resolve: function(rootValue, args) {
        let author = _.clone(args);
        return authorsCollection.insert(author)
          .then(_ => author);
      }
    }
  }
});

const Schema = new GraphQLSchema({
  query: Query,
  mutation: Mutation
}); 

export default Schema;

Any help would be much appreciated, Thanks :)

application/graphql requests being incorrectly handled?

I'm guessing that I'm doing something dumb here, but I can't see it :( Would appreciate some pointers!

Requesting via the JSON protocol works great:

> curl -H "Content-Type:application/json" -XPOST -d '{"query": "{ currentUser { id } }"}' http://localhost:3000/graphql
{
  "data": {
    "currentUser": {
      "id": "f9342aa2-7674-4050-9e6c-559f14c46b52"
    }
  }
}

But when I attempt to use the application/graphql protocol, I get:

> curl -H "Content-Type:application/graphql" -XPOST -d "{ currentUser { id } }" http://localhost:3000/graphql
{
  "errors": [
    {
      "message": "Must provide query string."
    }
  ]
}

Need to inject network layer in Relay to work with session

By default Relay does not send any cookie and so express-graphql middleware won't work with session. We need to explicitly configure the default network layer with this piece of code in the app entry point before calling ReactDOM.render:

Relay.injectNetworkLayer(
  new Relay.DefaultNetworkLayer('http://localhost:3000/graphql', {
    credentials: 'same-origin',
  })
);

It would be great to document this somewhere because for the moment it seems that session can work out of the box with GraphQL.

support graphql request metrics trace

In graphql,all request is POST and in the same url.

Because of this,the server side can not distinguish which request is slow.

In below query,is there any way to get the TestQuery value before graphql execute so I can trace the query name and log it's cost time?

Query TestQuery{
  ping
}

Firefox with Relay opens GraphiQL instead of process query

I found a function which is making decision about GraphiQL:

/**
 * Helper function to determine if GraphiQL can be displayed.
 */
function canDisplayGraphiQL(request: Request, data: Object): boolean {
  // If `raw` exists, GraphiQL mode is not enabled.
  var raw = request.query.raw !== undefined || data.raw !== undefined;
  // Allowed to show GraphiQL if not requested as raw and this request
  // prefers HTML over JSON.
  return !raw && request.accepts([ 'json', 'html' ]) === 'html';
}

Requests that Relay send aren't "raw". BTW what is "raw"?
Firefox prefers HTML.

Resolve field based on another field that return a promise

Let's assume I have the type below

const myType = new GraphQLObject({
  name: "MyType",
  fields: {
     foo: {
        type: GraphQLBoolean,
        resolve: (_) => serviceCall()
     },
    bar: {
       type: GraphQLString,
       resolve: (obj) => obj.foo ? "bar1" : "bar2"
     }
  } 
})

foo is a boolean field and serviceCall return a promise. I want to be able to compute bar based on the value of foo. My problem is that obj.foo is always undefined.
I would like to be able to do something like:

resolve: (obj) => {
   return obj.foo.then((value) => value ? "bar1" : "bar2")
}

Future plans for this repository

What is the roadmap/future plans for this repository? I'm working on a GraphQL HTTP server and I'm evaluating whether I should use this project or build my own. I am a little confused on whether this repository is supposed to be a reference implementation for GraphQL HTTP server authors or is this supposed to be the resource for building production grade HTTP GraphQL servers with GraphQL JS. If it is not the latter I ask if that might be reconsidered.

There are a few features which I would like to see before using this in production (in order of importance):

  • Remove the express dependency. This module could easily be just a connect-style middleware which could then work with express, connect, router, and koa (off the top of my head). This might warrant a module name change to graphql-http or something.
  • Query cache for production. These queries would be pre-parsed/pre-validated and they would be referenced using their operation name by a client.
  • By default add an extra validation rule to prevent malicious queries (could be implemented in another module and imported).

These are things I would be willing to contribute (as I would be building them on my own anyway…). If I get approval from the current core team I can start developing these things. I would rather contribute to this repository over creating a new one or keeping the code close sourced.

GraphiQL allow sending HTTP headers within this project implementation.

How can I send custom headers from the GraphiQL client ?

On every client REST APIs it's possible to define headers for the HTTP requests. How can I do the same with this project ?

An API client (GraphiQL) without the possibility to set headers but only the data ? Really ? I can understand GraphiQL is network protocole agnostic when installed as a standalone, but not in the context of this lib ! Indeed express-graphql means http-graphql.

On GraphiQL there is a "Query variables" sections so why not adding a "HTTP headers" section too ?
It's a serious need for many of us.

PS: Please don't tell me to install directly the GraphiQL from this repo graphql/graphiql ... while precisely the purpose of express-graphql is to have an easy and ready to use package preventing to bother with installing each other libs as a standalone.

Add middleware to transform req.query

Hi !

I have a compressed query in req, and I want to decompress it before send to GraphiQL. Why this code doesn't work ? Is it possible ?

router.use("/debug", (req, res, next) => {
  const decompressed = lz.decompressFromEncodedURIComponent(req.query.query);
  req.query = {
    query : decompressed,
  };
  next();
}, require("express-graphql")({
  schema : graphqlRootSchema,
  graphiql : config.get("NODE_ENV") !== "production",
  pretty : true,
  formatError : format.fromGraphiql
}));

Thx !

`rootValue` no longer being passed

After upgrading to latest React NPM packages, rootValue it's no longer being passed:

  • "express-graphql": "^0.5.3",
  • "graphql": "^0.6.0",
  • "graphql-relay": "^0.4.2",
  • "react-relay": "^0.9.0",
  • "babel-relay-plugin": "^0.9.0",

Intercepting graphiQL requests

I have a fully stateless backend (token only). Using graphIQL doesn't work because I have no way to pass in my token.

Is there a way to intercept my incoming graphiQL tokens so I can add the token?

Flow Definitions Removed During Compile/Build

I'm using Flow in my project & I'd like to use all the flow definitions from express-graphql, yet they are removed during the build script.

Disclaimer: I'm unfamiliar with how Travis CI works.

I know I can manually download the project to get access to the 'src' directory where the definitions are, but that seems a bit weird. I'd like to use npm install to maintain all my packages + preserve the flow type definitions.

What is the best practice for preserving flow definitions using npm install? & is there a way to do so that is not package specific & would apply to all flow typed libraries?

Bad escape of query result on initial load of page

We're running into an issue with GraphiQL that our QA team uncovered where the page won't properly render when the result of the query specified in the query-string "query" parameter includes HTML as a string and it is injected into the page on initial load:

untitled

The current workaround is to copy and paste the query into a new page w/o query string or remove the query string and reload, so that the query is loaded from the localStorage.

A way to post-process the response

Would be nice if there was some way of post-processing the response. Some use cases:

  • Log errors
  • Sanitize error messages from any sensitive information
  • Return 400 on validation error; 500 on any other error
  • Add custom response headers based on response body

ReferenceError: Promise is not defined

I am following the tutorial http://graphql.org/docs/getting-started/,

and when I tried to hit the http://localhost:3000/graphql, I get the error like this:

ReferenceError: Promise is not defined
at /gls16_api/node_modules/express-graphql/dist/index.js:66:9
at Layer.handle as handle_request
at trim_prefix (/gls16_api/node_modules/express/lib/router/index.js:312:13)
at /gls16_api/node_modules/express/lib/router/index.js:280:7
at Function.process_params (/gls16_api/node_modules/express/lib/router/index.js:330:12)
at next (/gls16_api/node_modules/express/lib/router/index.js:271:10)
at expressInit (/gls16_api/node_modules/express/lib/middleware/init.js:33:5)
at Layer.handle as handle_request
at trim_prefix (/gls16_api/node_modules/express/lib/router/index.js:312:13)
at /gls16_api/node_modules/express/lib/router/index.js:280:7

anyone can help?

Better error stack in response

Would be nice if the error stack in responses could contain more detailed information.

I incorrectly imported some functions for my schema and undefined is not a function without any line numbers was very painful to track down the source of the problem.

Warning: Server request for query `AppHomeRoute` failed for the following reasons:

1. undefined is not a function
   query AppHomeRoute{game{id,..._0c28183ce}} fragment _1452748
                      ^^^

I'm sure that more experience using express-graphql would make this error's location far easier to find based on the flagging of game{. I had difficulty finding the actual function that was undefined as I only knew to look somewhere within the game query type.

https support

Would https support be possible with this repo or would that be set up with Express before the main constructor from this project gets passed to express.use?

Custom logo

Hi there, fantastic project. One feature that would be great is the ability to put custom logos or at least some custom text into the logo space.

Cheers.

GraphQL Mutation returns 404 outside of graphiql. May have to do with express-graphql

I recently worked on a mutation, tested it out in graphiql and it checked out. However when attempting to hook it up the a frontend application it fails. I made a SoF post about this, not sure if it is express-graphql. The error at hand is that on this particular mutation for some reason the response payload is "Cannot POST /" along with a 404 code. If you happen to know what may be causing this please comment.

Response with form validation errors

Let's say you have a <form> which posts data to API server. The API server validates the input and returns JSON object. If the input is invalid (e.g. invalid email or email already exists in database) an error objects {errors: {email: "already exists"}} is returned.
How do we handle and serve these kind of errors when using GraphQL? How and where should data validation be implemented (should that be part of my GraphQL code, maybe inside each resolve function)?

_graphql.Source is not a function

I get

{
  "errors": [
    {
      "message": "_graphql.Source is not a function"
    }
  ]
}

response when running a query (as described in the readme). If I do console.log(_graphql.Source) it says undefined. The graphql(schema) command works so my schema is ok. Bug?

0.5.2 changes module.export

The symbol exported when using commonjs style require has changed.

In 0.5.1:

> require('express-graphql')
[Function: graphqlHTTP]

In 0.5.2:

> require('express-graphql')
{ default: [Function: graphqlHTTP] }

Something related to how es modules are transpiled to commonjs I guess.

Example with multer

Can you provide an example how to use this middleware with multer?

I do not understand how to correctly handle file uploads.

text/json used in sendError

Reading the source I noticed text/json was used in sendError instead of application/json. Is this correct?

Opt-in to accepting mutations on GET

Performing mutations on an http GET request is typically bad behavior. It's usually only presumed safe to do this if providing some single-use token to avoid CSRF attacks.

express-graphql should not execute mutations on GET requests unless some opt-in is provided.

How to put the request body into context?

I'm currently attempting to get the request body into context, because part of the body contains a JWT that needs to be decoded. However when I try the following I get undefined for context:

app.use('/', graphqlHTTP((req) => ({
  schema: Schema,
  context: req.body,
  pretty: true,
  graphiql: false
})));

I logged out req and I didn't see body in there. I'm using a react-reach, and adding the following to the body on the request:

{
  query: {...},
  queryParams: {...},
  options: {
    token: '...'
  }
}

I know the body is being interpreted because my queries/mutations that are in the body are being interpreted and executed. Just can't seem to find it when passed to context.

Request fails when using application/graphql with body parser

When sending a Content-Type : application/graphql with a query string the request fails.
If using body parser json or url encoded it says that i don't have a query string.
if used with text type application/graphql it gets stuck on parseBody.js in the line 119.

Supplying operationName leads to "Must provide an operation" error

When I run this query

http://localhost:3001/data?operationName=query&query=%0A%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20count%0A%20%20%20%20%20%20%7D%0A%20%20%20%20

The server returns a 400, {"errors":[{"message":"Must provide an operation."}]}

However without the operation name (as below) it works fine:

http://localhost:3001/data?query=%0A%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20count%0A%20%20%20%20%20%20%7D%0A%20%20%20%20

package.json

{
  "name": "stks",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "dependencies": {
    "babel-polyfill": "^6.5.0",
    "body-parser": "^1.15.0",
    "express": "^4.13.4",
    "express-graphql": "^0.4.10",
    "graphql": "^0.4.18",
    "isomorphic-fetch": "^2.2.1",
    "query-string": "^3.0.1",
    "react": "^0.14.7",
    "react-dom": "^0.14.7",
    "react-redux": "^4.4.0",
    "react-router": "^2.0.1",
    "redux": "^3.3.1",
    "redux-thunk": "^2.0.1"
  },
  "devDependencies": {
    "babel-core": "^6.5.2",
    "babel-loader": "^6.2.2",
    "babel-preset-es2015": "^6.5.0",
    "babel-preset-react": "^6.5.0",
    "babel-preset-react-hmre": "^1.1.0",
    "gulp": "^3.9.1",
    "nodemon": "^1.8.1",
    "pm2": "^1.0.1",
    "raw-loader": "^0.5.1",
    "style-loader": "^0.13.0",
    "webpack": "^1.12.13",
    "webpack-dev-middleware": "^1.5.1",
    "webpack-dev-server": "^1.14.1",
    "webpack-hot-middleware": "^2.7.1"
  },
  "scripts": {
    "start": "NODE_ENV=dev node index.js",
    "build": "webpack && gulp build",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

Thanks

Allow to know when a query has finished being processed

I've seen #36 but my need is far more simple. I'd love to have a way to know when a GraphQL query has finished being processed, so that my custom-built logging library can "close the story" of the request (I'm using hierarchical stories). I'm not interested in the result of the request, not even if it was successful or threw an error, just in the fact that it ended.

I see two ways to accomplish this:

  1. The middleware returns its main promise:

      expressGraphql = require 'express-graphql'
      graphqlMiddleware = expressGraphql (req) ->
        schema: gqlServer.getSchema()
        rootValue: {story: req.logStory}
    
      expressApp.use '/api/graphql', (req, res) ->
        graphqlMiddleware req, res
        .finally -> req.logStory.close()
  2. The middleware accepts a callback:

      expressApp.use '/api/graphql', expressGraphql (req) ->
        schema: gqlServer.getSchema()
        rootValue: {story: req.logStory}
        done: -> req.logStory.close()

I believe the second alternative is better, since it is simpler and doesn't involve leaking the middleware's implementation details (the fact that it uses promises).

I can propose a PR if you find this idea useful.

Support CORS enabled servers and OPTIONS requests

  // GraphQL HTTP only supports GET and POST methods.
    if (request.method !== 'GET' && request.method !== 'POST') {
      response.set('Allow', 'GET, POST');
      return sendError(
        response,
        httpError(405, 'GraphQL only supports GET and POST requests.'),
        pretty
      );
    }

Currently if the Express app enables CORS express-graphql will fail due to the above check sending a 405 error when the OPTIONS request comes in.

Two options I see:

  1. Create a flag for enabling CORS in express-graphql and enable OPTION support when true
  2. Just support OPTION requests by default

I'd be happy to implement either, or another option, pending approval!

rootValue: request.session,

Hi,

app.use('/graphql', graphqlHTTP(request => ({
schema: MySessionAwareGraphQLSchema,
rootValue: request.session,
graphiql: true
})));

How do I access the rootValue at schema.js?

Access variables

Are the GraphQL variables passed in through a query exposed in any way? I'd like to pass in a token of some sort and it would be easy through a variable. If not, I could use a header.

Folder structure

I was working this graphql and express and reach the moment than I can not hold all fields definitions in schema.js, so I tried to create the folder structure for this case. Which you can reach by link.
I would be glad to discuss this question. Thank you.

Correct mime-type for JSON?

I noticed the middleware returns JSON with text/json mime-type. Any reason why it doesn't return application/json?
I know there is a debate about that for a long time in the community but I thought application/json became the norm.

Custom GraphiQL middleware

Hi Lee,

I just finished draft version of GraphiQL that supports HTTP headers.
I know there are multiple discussions about keeping the original GraphiQL version lean and protocol independent, which makes sense to me.

So the question is what's the best way to structure the middle ware here, so we can specify our own custom versions.

At the moment I'm using non flexible solution:
graphQLServer.use('/graphiql', express.static('graphiql'));

But I would want to see something like this possible:

import graphiql from 'graphiql-headers';
app.use('/graphql', graphqlHTTP(request => ({
  schema: MySessionAwareGraphQLSchema,
  context: request.session,
  graphiql: graphiql
})));

What would be accepted as PR?

Thanks for your time on this.

Stack traces

Hi,

To enable stack traces, the documentation recommends to provide an error => error function to the formatError option. However, like most GraphQLError properties, stack is not enumerable and therefore not visible when converting the error object to a JSON string .

So currently the only way to get useful info is to implement a custom format function like this one.

Wouldn't it be simpler to make the error details enumerable (stack, nodes, locations, etc.) in GraphQLError? If that's not possible, why not providing a common debug function in this package?

Can't access session custom element.

I can't access session data through graphQLHTTP
My session look like this:

Session {
  cookie:
   { path: '/',
     _expires: Sat Jan 09 2016 20:51:51 GMT-0800 (PST),
     originalMaxAge: 600000,
     httpOnly: true 
  },
  viewer: '[email protected]' 
}

But if I access like this"

app.use('/graphql', graphQLHTTP(request => ({
    schema: querySchema,
    rootValue: { session: request.session },
    graphiql: true,
    pretty: true,
})));

This is what I got:

Session {
     cookie:
      { path: '/',
        _expires: Sat Jan 09 2016 20:51:51 GMT-0800 (PST),
        originalMaxAge: 600000,
        httpOnly: true 
      } 
   } 
}

broken import for typescript

Unable to require the module using import in Typescript after this change: #92 . It compiles, but seems like the module doesn't have any default export.

Code:

import graphqlHTTP from 'express-graphql';
import schema from './schema';

const graphqlMiddleware:any = graphqlHTTP({
  graphiql: true,
  pretty: true,
  schema: schema,
});

runtime error:

const graphqlMiddleware = express_graphql_1.default({
                                                                           ^
TypeError: express_graphql_1.default is not a function

tsconfig:

"compilerOptions": {
        "target": "es2015",
        "module": "commonjs",
        "moduleResolution": "node"
}

GraphiQL Auth

Set fetcher to use withCredentials to allow cookies to be sent for auth.

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.