What is GraphQL and How Did It Evolve From REST and Other API Technologies?

Continued from page 1.

As you move through this part of our series, you may encounter GraphQL examples whose syntax isn't explained line-for-line. Don't worry. We will come back to it. The nice thing about GraphQL is that the syntax requires no special knowledge to master other than understanding the structure of the data entity and its subordinates to return as well as how to define input parameters. There are no special keywords such as SELECT, FROM, GROUPBY, JOIN that you typically find in SQL. GraphQL is simply about defining an object graph.

A Schema Defines an API Implementation

A schema is the main organizational unit of GraphQL. The schema contains the type declarations, resolvers and subscriptions. You can think of the schema as the structure that describes a GraphQL API and makes it operational. The following is an example of a formal expression of a GraphQL schema as taken from the demonstration application for this series.

const schema = {
    typeDefs,
    resolvers,
    subscriptions};

Representing Data Models as Object Types

The way that data structures are defined in GraphQL is according to custom object types. Objects types are described using an object description format special to GraphQL. The structure of the format is as follows:

WHERE

type TypeName {
  fieldName: fieldType
  fieldName: fieldType
  fieldName: fieldType
}

WHERE

type is a GraphQL reserved word

TypeName is the name of the type. This name can be a GraphQL operation such as Query, Mutation or Subscription. Also, similar to a JSON object, the TypeName can name a custom object type, for example Actor or Movie.

fieldName is the name of a field in the object, for example id, firstName or lastName. If the containing type of the fieldName is a Query, each fieldName will describe a particular query published by the API. If the containing type is a Mutation, each fieldName will describe an mutation published by the API. If the containing type is a Subscription, each fieldName will describe the behavior for message transmission to external parties subscribed to the event

In addition to supporting specific and custom type objects, the GraphQL specification supports the scalar types, String, Int, Float, Boolean and ID, which denotes a unique identifier. Also, the specification supports arrays of scalar values and object types. For example, [String] indicates an array of the scalar value, String. [Actor] indicates an array of the custom object type, Actor.

All values in GraphQL are declared explicitly according to type. GraphQL does not support implicit type declaration.

Listing 8 below shows a declaration of the custom object type, Person.

type Person {
   id: ID
   firstName: String
   lastName: String
   dob: Date
   knowsConnection: [Person]
   likesConnection: [Person]
   marriedToConnection: [Person]
   divorcedFromConnection: [Person]
}

Listing 8: The type, Person is an example of the custom object type described in GraphQL's type definition format

Let's take a look at the details of the declaration of the type, Person shown above in Listing 8. The type, Person publishes eight fields: id, firstName, lastName, dob, knowsConnection, likesConnection, marriedToConnection, divorcesFromConnection. The field, id is of type, ID. ID is a built-in scalar type special to GraphQL. ID is intended to a describe a unique identifier. An ID is typically a string, but GraphQL expects that the string is a UUID and not human readable.

The fields, firstName and lastName are of scalar type String. String is another one of the types built-in to GraphQL. The field, dob is of type Date. Date is a custom scalar. GraphQL allows you to define custom scalar types. Custom scalars are useful in situations in which a single value with special validation and parsing rules need to be supported. The fields, knowsConnection, likesConnection, marriedToConnection, divorcesFromConnection are arrays of Person types, as denoted by the square brackets.

The concept of connections is one that is evolving in GraphQL. Conceptually you can think of a connection as an association between two objects in an object graph. (The term, edge is used in discrete mathematics to indicate an connection between two nodes.) A convention is developing among GraphQL developers in which a category of an edge that exists between two nodes is called a connection, with the naming convention being, categoryConnection, hence knowsConnection, where the name indicates that the connection between two nodes is that one node knows the other.

We're going to take a in-depth look at connections as well a pagination techniques for controlling large lists associated with a connection in Part 3 of this series; How To Design, Launch, and Query a GraphQL API Using Apollo Server.

GraphQL Operations

GraphQL supports 3 basic operations, Query, Mutation and Subscription. These operations are first level fields in the GraphQL type system definition. The sections that follow provide examples of the basic operation typpes both in terms of declaration and execution using the GraphQL query language.

Query

A Query, is as the name implies, an operation type that has fields that describe how to get data from a GraphQL API. For those who are familiar with HTTP-based APIs, a GraphQL query most closely correlates to an HTTP GET. Listing 9, below shows an example of the implementation of a Query type in GraphQL's type definition format. It relies on the Person type which was defined in Listing 8, above.

type Query {
   persons: [Person]
   person(id: ID!): Person
   movies: [Movie]
   movie(id: ID!): Movie
   triples: [Triple]
   triplesByPredicate (predicate: Predicate!): [Triple]
}

Listing 9: Each property in a Query type describes a query for getting from the GraphQL API.

You'll notice that in Listing 6 above there are a number of fields defined within the type, Query. Each field in the Query operation type describes a particular query supported by the API. The fields, persons defines a query literally named, persons that returns an array of Person objects. (Again, an array is indicated by the square brackets.) The field, person(id: ID!) indicates a query named, person that has a parameter, id of type ID. The exclamation symbol means the that a value must be provided for the parameter. The query will use the value assigned to id to do a lookup for the particular person. (Please be advised the naming the unique identifier parameter id, is a matter of convention used by most implementation. That the name happens to be lower case of the type ID is purely coincidental.)

Defining query parameters in the way shown above is part of the GraphQL specification. Later on, in Part 2 of this series will take a look at how the Apollo Server implementation of GraphQL passes query parameter values onto actual query behavior. The important thing to understand now is that you declare parameters by name and type within a set of parentheses in the field definition of a particular query.

As you can see, the pattern for declaring a query that returns an array and query that returns an object also applies to other fields in the Query definition. The query, movies returns an array of Movie objects. The query movie(id: ID!) returns a particular Movie.

However, notice that while the query, triples supports the "plural" pattern by returning an array of Triple objects, the query triplesByPredicate(predicate: Predicate!) is different for two reasons. (A Triple is custom object created for the demonstration application that accompanies this series. Triple is not a reserved keyword in GraphQL.) First, the name of the query, triplesByPredicate differs from the convention we've seen thus far. The usual pattern for query naming is plural and singular according to type, — movies and movie, for example. Yet, triplesByPredicate violates this convention. This is OK because there will come a time when some queries will need to be quite specific. There is nothing in the GraphQL that dictates how queries need to be named. The plural/singular pattern is conventional.

The second difference to notice about triplesByPredicate(predicate: Predicate!) is that it has a query parameter that is not a unique identifier. In fact the parameter, predicate, which is required as indicated by the exclamation symbol, is of type Predicate. Predicate is a custom enumeration type particular to the demonstration application. (GraphQL does support custom enumerations.) The enumeration, Predicate, is defined as follows:

 enum Predicate {
	KNOWS
	LIKES
	WORKED_WITH
	MARRIED_TO
	DIVORCED_FROM
}

Thus, the query, triplesByPredicate(predicate: Predicate!) translates into "Show me all triples according to the Predicate enumeration value passed to in the query parameter, predicate."

One of the benefits that GraphQL provides in terms of query declarations is that it allows you to define only the fields you want the query to return. With REST, the developer has no explicit control of the structure of the return data. On the other hand, GraphQL provides a great deal of flexibility. For example, imagine we want to query a GraphQL API for a collection of Person objects, but we want to the returned data structure to have only the firstName and lastName fields, the query we could write is:

{
  persons{
    firstName
    lastName
  }
}

Now imagine we want to query for a collection of firstName and Person objects again, only this time we want to get the fields, firstName and firstName, firstName and lastName and firstName and dob in the data structure that's returned, Also, we want to see an array of firstName and knowsConnection objects for each firstName and Person object returned according to the firstName and firstName and firstName and lastName of the person in the firstName and knowsConnection. In this case, we write:

{
  persons{
      firstName
      lastName
      dob
      knowsConnection{
            firstName
            lastName
        }
      }
}

Listing 10, below, shows example a variety of queries to get data for all persons in which each query declares a particular set of fields to return.

Query all Person objects in the system
QueryResult
{
  persons{
    firstName
    lastName
  }
}
"data": {
     "persons": [
        {
          "firstName": "David",
          "lastName": "Bowie"
        },
        {
          "firstName": "Nicholas",
          "lastName": "Roeg"
        },
        {
          "firstName": "Rip",
          "lastName": "Torn"
        },
        {
          "firstName": "Candy",
          "lastName": "Clark"
        }
      ]
}
{
  persons{
      firstName
      lastName
      dob
  }
}
{
  "data": {
    "persons": [
        {
          "firstName": "David",
          "lastName": "Bowie",
          "dob": "1947-01-08"
        },
        {
          "firstName": "Nicholas",
          "lastName": "Roeg",
          "dob": "1928-08-15"
        },
        {
          "firstName": "Rip",
          "lastName": "Torn",
          "dob": "1931-02-06"
        },
        {
          "firstName": "Candy",
          "lastName": "Clark",
          "dob": "1947-06-20"
        }
      ]
}
{
  persons{
      firstName
      lastName
      dob
      knowsConnection {
            firstName
            lastName
        }
      }
}
{
  "data": {
    "persons": [
      {
        "firstName": "David",
        "lastName": "Bowie",
        "dob": "1947-01-08",
        "knowsConnection": [
          {
            "firstName": "Nicholas",
            "lastName": "Roeg"
          }
        ]
      },
      {
        "firstName": "Nicholas",
        "lastName": "Roeg",
        "dob": "1928-08-15",
        "knowsConnection": [
          {
            "firstName": "David",
            "lastName": "Bowie"
          },
          {
            "firstName": "Rip",
            "lastName": "Torn"
          },
          {
            "firstName": "Candy",
            "lastName": "Clark"
          },
          {
            "firstName": "Buck",
            "lastName": "Henry"
          }
        ]
      }
    ]
  }
}

Listing 10: Developers can query a schema in a variety of ways according to the fields they want returned

Mutation

A Mutation operation type describes how to add, update or delete data in the API. In other words, the operation type describes how to mutate data in the API. The HTTP corollaries to Mutation are POST, PUT, PATCH and DELETE. Listing 9, below, shows a Mutation type that has methods for adding and updating an object type Movie, adding a Person and adding a Triple. (Movie, Person and Triple are custom object types. The exclamation character, !, indicates a required parameter.)

type Mutation {
   addMovie(movie: MovieInput!): Movie
   updateMovie(movie: KnownMovieInput): Movie
   addTriple(triple: TripleInput): Triple
   addPerson(person: PersonInput): Person
}

Listing 11: A Mutation names the various behaviors for adding, updating and deleting data from the API

Listing 12, below shows an example of syntax for executing the mutation, addPerson along with the results on the left side of the listing,

MutationResult
mutation{
  addPerson(person: {firstName: "Marlon",                                 
  lastName: "Brando",
  dob: "1924-04-03"})
  {
    id
    firstName
    lastName
    dob
  }
}
{
  "data": {
    "addPerson": {
      "id": "4ddac860-0769-42e3-9dd5-3901fbe33a11",
      "firstName": "Marlon",
      "lastName": "Brando",
      "dob": "1924-04-03"
    }
  }
}

Listing 12: The result of executing the mutation, addPerson()

The important thing to understand about mutations is they are intended to be used with custom object types that also are defined within the type system. Thus the mutation, addMovie as shown above in Listing 10, is intended to add an instance of the type, MovieInput to the API's datastore. An Input type is an abstraction particular to GraphQL that denotes a type that is to be used with a mutation to input data. An Input type aggregates data for input into a single object. (We'll cover the GraphQL Input type in detail in the upcoming Part 2 of this series; GraphQL APIs for Everyone: An In-Depth Tutorial on How GraphQL Works and Why It's Special.)

Subscription

A Subscription is an operation type that is required when supporting the PubSub pattern within a GraphQL API. A GraphQL API can publish one or many events available for subscription. This is useful in an event-driven real-time scenario when data is constantly being updated (each update is an event). An example, a use case might be a stock ticker where an end user wants to monitor, in real-time, price changes to a public stock. Each field in a Subscription describes a particular event generator that corresponds to an event publication . Listing 13 below, shows a description of the onPersonAdded event generator.

type Subscription {
    onPersonAdded(channelName: String): Event
}

Listing 13: GraphQL supports publishing events to which listener can subscribe

The event generator gets fired internally in the GraphQL API server each time an new Person is added using the API's addPerson mutation.

The asynchronicity built into subscriptions is one of GraphQL's superpowers. As is, REST does not include a subscription capability out of the box. Asynchronous messaging can be implemented with REST, but it's more of a bolt-on feature, usually requiring the use of a separate message publishing service such as RabbitMQ. Using both a REST service and a message publishing service invariably requires disruption to the typical API workflow. This disruption involves cutting over to a new API endpoint or even a change in technology altogether. gRPC, which is also not RESTful, is an example of another REST alternative that has subscription capability built-in. We'll discuss GraphQL subscriptions in more detail below.

Working With Resolvers

In addition to the type system definition, which some implementations call a typeDef, GraphQL also supports the concept of a resolver. A resolver defines functionality for Query, Mutation or Subscription definitions. The way to think about it is that the type definitions (typeDef) provides the description of the query in terms of name and results. The resolver provides the actual programming logic that the GraphQL server will use to execute the query.

For example, a Query that has the field, persons: [Person] will have a corresponding resolver field, persons. That field will be bound to a function that will do the work of getting the data necessary to fulfill the query, persons: [Person].

Listing 14, below, shows an example of the resolver, persons: which is implemented in node.js running on Apollo Server. The resolver is associated with an anonymous, asynchronous Javascript method that returns a collection of Person objects from the application's datastore.

persons: async (parent, args, context) => {
            return getCollection('persons');       
}

Listing 14: A resolver, persons is a method implemented in node.js/Javascript that returns a collection of Person types.

Revolver behavior is written according to the implementation framework. For example, should the particular GraphQL API be implemented as a NodeJS Apollo Server, the resolver will be written as a Javascript function. If the API is implemented in .NET, the resolver will be written in a language supported by the .NET CLR, such as C# or VB.NET.

Working with Subscriptions

Whereas RESTful APIs are intended to support stateless requests and response interactions in an asynchronous manner, GraphQL supports synchronous interactions via subscriptions. A subscription is a mechanism by which an API client registers with a GraphQL API to receive notifications according to events generated from the API.

Figure 7 below describes the way a client registers a custom subscription to receive event messages. In this case the event of interest is named, onPersonAdded. The example API fires the custom event, onPersonAdded when certain processes complete on the backend of the API. The API is programmed to fire onPersonAdded whenever a new person is added to the API's datastore.

Figure 7: Once a client registers a subscription, it will receive messages from the API when a predefined event(s) occur.

Figure 7: Once a client registers a subscription, it will receive messages from the API when a predefined event(s) occur.

The specifics of working with a subscription is as follows. (In this case, in Figure 5 above, the client is using, the browser-based Apollo Server GraphQL Playground.) (1) A GraphQL subscription statement is sent to the API. The subscription statement creates an connection to the API server and listens for messages associated with the event, onPersonAdded. (2) In a separate browser window, a custom mutation, addPerson is executed. A mutation, addPerson has been programmed on the server side to fire an onPersonAdded event and send a message when a person is successfully added to the API's datastore. (3) The first browser window which is listening for onPersonAdded messages synchronously receives the message that was generated when the person was added.

Adding continuous communication between clients and the server using subscriptions adds a new dimension to working with an API. Operationally, subscriptions make it possible for applications such as Facebook to implement real-time updates of comments and messages; a great feature that you'll probably recognize if you're a user of Facebook.

As mentioned previously a Subscription is a basic operation type that is part of the GraphQL specification. Thus, developers can create none, one or many subscription events within a given API. Once subscription events are defined, a developer will fire those events in related resolver methods, For example, firing an onPersonAdded event in the corresponding addPerson() node.js method

Event publishing can be a confusing subject, especially for those developers that are typically accustomed to writing API that use only synchronous request/response interactions. It takes time to fully absorb the concepts and practices of the event-driven programming paradigm. The important thing right is to understand is that GraphQL supports asynchronous event publishing by way of subscriptions. We're going to take a more detailed look at subscription in Part 3 which covers building a GraphQL API.

Support for Interfaces

In addition to object types GraphQL also supports a certain degree of inheritance by specifying interfaces and unions. Interfaces are common in object-oriented programming. An interface is a named abstraction that contains properties and methods. Once an interface, along with its properties and methods has been defined, those properties and methods are automatically supported by any new object that is initialized as an implementation of that interface.

When it comes to GraphQL, object types implementing a particular interface need to define the fields declared in the interface. Having to redefine fields in an object type that are already defined in the interface might seem like redundant work. But, such redefinition is a common task in any programming language the supports interfaces. The difference with GraphQL is that, whereas in an object-oriented programming language such as Java an interface will define methods that need be provided by classes implementing the interface, in GraphQL object types do not define methods directly. (Remember, implementing the behavior of an object type is done in a resolver.) Implementing an interface in GraphQL can indeed seem as if you are doing a redundant work but it's worth it because you can query GraphQL according to an interface as you can see in Listing 16, later on.

Listing 15, below, shows how the interface, Personable is implemented in the object types, Person and Actor. Lin

 interface Personable {
	id: ID
	firstName: String
	lastName: String
	dob: Date
}

type Person implements Personable{
	id: ID
	firstName: String
	lastName: String
	dob: Date
	marriedTo: Person
}
    
type Actor implements Personable{
	id: ID
	firstName: String
	lastName: String
	dob: Date
	roles: [Role]
}

Listing 15: The Person and Actor object types implement the interface, Personable

Listing 16, below, shows how to write a query in GraphQL that returns information according the fields defined in an interface. In this case we're running the movies query, but retrieving data according to the fields in the interface, Film. The ellipsis (...) indicates that the query is using a GraphQL inline fragment.

{
  movies{
    title
    actors{
      ... on Personable{
        firstName
        lastName
      }
    }
  }
}
{
  "data": {
    "movies": [
      {
        "title": "The Man Who Fell to Earth",
          "actors": [
            {
              "firstName": "David",
              "lastName": "Bowie"
            },
            {
              "firstName": "Rip",
              "lastName": "Torn"
            },
            {
               "firstName": "Candy",
              "lastName": "Clark"
            },
            {
              "firstName": "Buck",
              "lastName": "Henry"
            }
          ]
        },
        {
          "title": "Performance",
          "actors": [
            {
              "firstName": "Mick",
              "lastName": "Jagger"
            },
            {
              "firstName": "James",
              "lastName": "Fox"
            },
            {
              "firstName": "Anita",
              "lastName": "Pallenberg"
            },
            {
              "firstName": "John",
              "lastName": "Bindon"
            }
          ]
        },
        {
          "title": "Dr. Strangelove",
          "actors": [
            {
              "firstName": "Peter",
              "lastName": "Sellers"
            },
            {
              "firstName": "Slim",
              "lastName": "Pickens"
            }
          ]
        },
        {
          "title": "Being There",
          "actors": [
            {
              "firstName": "Peter",
              "lastName": "Sellers"
            }
          ]
        }
    ]
  }
}

Listing 16: Creating a query according to an interface

Support for Unions

A union is another GraphQL abstraction that provides a way to return data according to a variety of concrete types. Unlike interfaces that can be used to describe common fields between types, a union provides a way to create a GraphQL query that returns concrete types that have different field definitions.

Unions are declared in a type definition, as shown Listing 17 below. The meaning of the example is, define a queryable type, SearchResultObject that will return back data that is in either the Person object type or the Actor object type.

union SearchResultObject = Person | Actor

Listing 17: A union is a type that combines object types with differing fields.

Creating a query according to a union is a bit tricky in that you need to use inline fragments to get a desired result. For example, the union, SearchResult as shown above in Listing 16, will return fields that are in both the types Person or Actor. (Person and Actor are shown above in Listing 17.) The type Person has a field, marriedTo, while the type Actor has a field roles which is a collection of Role objects. (A Role object describes the movie and a character in that movie.).

Listing 18 below shows the objects in play for utilizing a union; the query required to get the data according to the union and the results of the query.

Server side type definitions
(Comments in tripe quotes)
Client side query in GraphQL
"""
Create a search result object that returns fields either in the Person type or Actor type
"""
   union SearchResultObject = Person | Actor

   type Person implements Personable{
        id: ID
        firstName: String
        lastName: String
        dob: Date
        marriedTo: Person
    }
    
    type Actor implements Personable{
        id: ID
        firstName: String
        lastName: String
        dob: Date
        roles: [Role]
    }

   type Role {
     character: String!
     movie: Movie
   }

"""
Define a query on the server side type definition that returns an array containing either Actor or Person types according to last name.
"""
type Query {
 getPersonActor(lastName: String!): [PersonActorSearch]
}
{
  getPersonActor(lastName: "Bowie") {
    ... on Person {
      firstName
      lastName
      marriedTo {
        firstName
        lastName
       }
    }
    ... on Actor {
      firstName
      lastName
      roles{
        character
        movie {
          title
        }
      } 
    }
  }
}
Query Results
{
  "data": {
    "getPersonActor": [
      {
        "firstName": "David",
        "lastName": "Bowie",
        "roles": [
          {
            "character": "Thomas Jerome Newton",
            "movie": {
              "title": "The Man Who Fell to Earth"
            }
          }
        ]
      },
      {
        "firstName": "Donnie",
        "lastName": "Bowie"
        "marriedTo: {
          "firstName": "Emilia",
          "lastName": "Bowie"
        }
      },
      {
        "firstName": "Emilia",
        "lastName": "Bowie"
        "marriedTo: {
          "firstName": "Donnie",
          "lastName": "Bowie"
        }
      }
    ]
  }
}

Listing 18: Defining and executing a query based on a union

Listing 18, above shows only the logic executed on the query side to get the desired data. There is still a good deal of work that needs to be done to create a server-side resolver that actually implements the behavior that returns the expected data according to the query and union specification. This is complex work that requires an advanced understanding of general programming logic in general and GraphQL in particular. The important things to understand on an introductory basis is that a union provides a way to query data according to fields that are not common between object types.

Introspection Makes Schemas Discoverable

One of the great benefits of GraphQL is that it provides a feature called introspection that makes it easy to discover the structure of a schema of an API at runtime. Technologies implementing GraphQL can use introspection to generate documentation for use by humans, as shown below in Figure 8.

Figure 8: GraphQL introspection make it easy to generate API documentation out of the box

Figure 8: GraphQL introspection make it easy to generate API documentation out of the box

Proponents of GraphQL will likely argue that this is another advantage over REST. Whereas RESTful APIs primarily rely on a completely separate description (eg: an OpenAPI description) for automation of documentation or generation of SDKs, GraphQL APIs have this capability built-in to them (the same is true of gRPC APIs). Also, very much like OpenAPI-based descriptions of RESTful APIs, introspection makes machine readable discovery possible. For example, all one machine needs to do to discover the details of a GraphQL API of interest is to configure and execute a __schema query, as shown in the left panel below in Listing 15. As within any query executed in GraphQL, you can define the fields that you want to return in the query result. When it comes to running a__schema query for types, the list of fields you can is quite large, and each item in the list of fields can have it's own list of fields. (The type object publishes the fields: kind, name, description, fields, interfaces, possibleTypes, enumValues, inputFields, ofType.) Thus, the result of all data available in the entirety of a running the query, __schema can go on for pages.

For the purpose of concise demonstration, Listing 19 below shows the types that exist in this article's demonstration application, displaying only the field, name of the type. The query is in the right panel. The result of running the introspection query is in the left panel of the listing.

{
  __schema {
    types {
      name
    }
  }
}
#A partial display of introspection output
{
  "data": {
    "__schema": {
      "types": [
        {
          "name": "Query"
        },
        {
          "name": "CursorPaginationInput"
        },
        {
          "name": "String"
        },
        {
          "name": "Int"
        },
        {
          "name": "Persons"
        },
        {
          "name": "Person"
        },
        {
          "name": "Personable"
        },
        {
          "name": "ID"
        },
        {
          "name": "Date"
        },
        {
          "name": "PersonConnection"
        }

.
 .
  .
}

Listing 19: A partial display of introspection output when running a __schema query for all the types in the API showing only the name field.

Being able to determine the characteristics of a GraphQL API at runtime to a fine grain opens up a host of possibilities, particularly as machine to machine interactions, the Internet of Things (IoT) and AI-powered bots continue to proliferate on the internet.

Conclusion

GraphQL is a transformational technology in terms of API design and implementation. It achieves a central goal of information exchange that's been around since the early days of commercial IT: to have an easy way to share hierarchical information quickly, in a well-known manner, according to common formats. As with any technology, there are benefits and limitations around its use. GraphQL is a transformational technology in terms of API design and implementation. It achieves a central goal of information exchange that's been around since the early days of commercial IT: to have an easy way to share hierarchical information quickly, in a well-known manner, according to common formats. As with any technology, there are benefits and limitations around its use.

Benefits

The most significant operational benefit of using GraphQL is that GraphQL, by design, cuts down on network traffic between client and server significantly. Unlike REST, which can require a number of round trips on the network to get all the data required to satisfy a particular need, when using GraphQL developers declare a query according to a specific object graph that gets sent to a GraphQL API once. The declared graph can be as shallow or deep into the object model as is required to meet the need at hand. Thus, delving into a particular object graph requires no continuing back and forth between GraphQL client and server except in cases of special paging scenarios. And, any data updating that needs to take place in real time can be accomplished asynchronously using subscriptions — a real-time feature that, unlike with REST which requires a bolt-on approach (eg: Websockets, Webhooks, HTML5 Server Side Events [SSE], etc.), is native to GraphQL.

Depending on your point of view, another benefit of GraphQL versus REST (covered later in this series) has to do with its dependence on explicit (vs. implicit) field types. In GraphQL (which relies exclusively on JSON-based data formatting), field types must be declared for all fields thereby leaving no room for ambiguity (a feature that many developers prefer). REST, on the other hand, with its allowance for implicit field typing, is more forgiving and fungible (which other developers appreciate). But the resulting ambiguity can also make it harder to debug interactions between distributed components (as are typical of API-led environments).

Similar to REST, another benefit of GraphQL is that the specification is technology agnostic. Being agnostic means that businesses do not have to convert their IT department to use a new language or framework. Companies can just adopt GraphQL using implementations in which they already have the expertise, such as Java, Python, Ruby, .NET or Javascript.

The GraphQL type system is easy to understand. The learning curve for mastery is minimal. All that's required is have an operational facility with object types, resolvers, and subscriptions. And, that programming resolvers is implementation specific means that companies can leverage existing programming skills and database technologies when creating behaviors for revolvers.

Finally, introspection, which, unlike with REST, is built into GraphQL. Neither humans nor machines have to hunt for a separate file that contains the details of a GraphQL API's design (there's no standard for where this file is kept in the REST world). It provides a means of discovery for both humans and machines. Introspection makes comprehensive documentation a first order feature in GraphQL. Also, introspection makes it possible to have a more sophisticated machine to machine interactions. Companies can use AI to create automation that can inspect a given GraphQL API to determine not only the data the API publishes, but also to determine how to extract and inject the data into the API's datastore.

Limitations

However, GraphQL does have limitations. The first drawback is that GraphQL does not support cross-domain queries. For example, it's not yet possible to construct a query that gets data from both Facebook and Twitter simultaneously. Thus, should a developer want to aggregate data between two domains, he or she must go back to having to make two or more calls to two or more data sources and aggregate accordingly.

The second drawback is that GraphQL is, for the most part, a technology that is focused on mobile devices. The people at Facebook created GraphQL as a better way for the company's iOS and Android developers to get the data needed to meet the needs of the mobile users. Making GraphQL fully and easily machine-readable, although possible, is still a vision yet to be realized.

Finally, GraphQL has does not yet fully support the promise of the Semantic web. The semantic web is about publishing data over the internet in a way that is machine readable, machine-discoverable and machine understandable according to mechanisms that are well-known and universal. HTML, XML and REST are still trying to achieve that promise. GraphQL is just starting out and has a way to go, particularly when it comes to accommodating information that is beyond the scope of a single domain.

GraphQL is Catching On

Yet, despite these shortcomings, each day more companies are adopting GraphQL as the preferred way to make their information available to others. Companies are redefining the nature of their APIs to be about working with object graphs rather than working with RESTful resources. It's a fundamentally different way of thinking. And, GraphQL's support for synchronous communication makes the notion of the continuously connected web a very real possibility within general IT.

GraphQL is an important step forward for companies that depend on APIs to do business. It's a technology that warrants a deeper investigation which we'll do in subsequent articles. The next article in this series will be an in-depth look at what it takes to build a real-world GraphQL API using Apollo Server. In addition to examining the details about the features described in this article, we'll look at advanced concepts such as pagination, connections, edge, and nodes. As those who have worked in production have come to learn, simple programs are nice for simple learning, but industrial strength understanding requires a greater degree of analysis.

Next: Part 2--GraphQL APIs for Everyone: An In-Depth Tutorial on How GraphQL Works and Why It's Special

Be sure to read the next GraphQL article: GraphQL APIs for Everyone: An In-Depth Tutorial on How GraphQL Works and Why It's Special

 

Comments (0)