graphqlapollo-servergraphql-jsgraphql-tag

What is the difference between gql and buildSchema?


What is the difference between graphql-js buildSchema and the apollo-server gql? They appear to do a very similar job.


Solution

  • Firstly note, apollo-server's gql is actually a re-export from graphql-tag. graphql-tag is a ~150 line package who's default and pretty much only useful export is gql.

    Quick bit of background: the graphql-js package provides an official reference implementation of GraphQL in Javascript. It provides two important things basically:

    1. The graphql function, which will process any GraphQL query against a GraphQLSchema object (the GraphQLSchema object is how graphql-js represents your parsed schema).
    2. A bunch of utility functions and types for building and validating the GraphQLSchema objects used by the graphql function from strings.

    Now, this is the part that ultimately confused me: in graphql-js, taking a string and turning it into a GraphQLSchema object is a two step process:

    It is like this is because the AST (Abstract Syntax Tree) is useful to a bunch of other tools which aren't even necessarily aware of GraphQL (for example see https://astexplorer.net/), where as the GraphQLSchema is only meaningful to graphql-js and related softwares.

    graphql-js exposes the functions for doing both steps and these are reused by downstream implementors (like Apollo). buildSchema is just a convenience wrapper to combine these two steps. It's literally this:

    /**
     * A helper function to build a GraphQLSchema directly from a source
     * document.
     */
    export function buildSchema(source: string | Source): GraphQLSchema {
      return buildASTSchema(parse(source));
    }
    

    The output of gql on the other hand is just the AST part, which is the same as the output from the graphql-js parse function. gql doesn't return a GraphQLSchema directly for the reason stated above: the AST is useful (note gql is also doing some basic stuff like stateful caching and will merge an array of schema string into a single AST result ...). Eventually the AST will have to be turned into a proper schema though, and apollo-server does this when the server is instantiated (presumably).

    To show the relation, we can take the output of gql (same as parse) run it through buildASTSchema (like buildSchema does) then pass it to graphql and it works the same:

    import { graphql, buildSchema, buildASTSchema } from 'graphql';
    import { gql } from 'apollo-server'; // equivalent to 'import gql from graphql-tag'
    
    const schemaString = `
      type Query {
        hello: String!
      }
    `;
    
    const a = buildASTSchema(gql(schemaString));
    const b = buildSchema(schemaString);
    
    graphql(a, '{ hello }', { hello: () => 'Hello world!' }).then((response) => {
      console.log(response);
    });
    graphql(b, '{ hello }', { hello: () => 'Hello world!' }).then((response) => {
      console.log(response);
    });
    

    Gives:

    { data: [Object: null prototype] { hello: 'Hello world!' } }
    { data: [Object: null prototype] { hello: 'Hello world!' } }