Hear from Salesforce leaders on how to create and deploy Agentforce agents.
Skip to main content
Contact Us 1-800-596-4880

This is Part 1 of the ProgrammableWeb API University Guide to GraphQL: Understanding, Building and Using GraphQL APIs

The desire to share structured data in a meaningful way has been a driving force behind information exchange since the first data entry clerk entered billing information into a mainframe computer. It's been a challenging task that has become even more difficult with the unyielding explosion of data created by the Internet. Fortunately, open standards such as TCP/IP, HTTP, XML, and JSON have made sharing data between different data domains easier. TCP/IP and HTTP (aka "the World Wide Web") provide common ways to move data between domains. XML and JSON have become the standard format by which to structure data. Also, as mobile devices and cloud-computing replace desktop PCs and bare metal servers in the client-server paradigm, we're seeing APIs based on these open standards become the way by which data is made available to consumers through Web and mobile apps. At the forefront of these API technologies are HTTP-based APIs, REST, gRPC and GraphQL.

Each of these API technologies have had a dramatic influence on how software is made and used in the age of the Internet. Any one of them is worthy of a book. The technology we're going to cover in this upcoming series of articles is the relative newcomer GraphQL.

Within ProgrammableWeb's API directory, GraphQL is one of the architectural styles that can be assigned to an API. REST and RPC are other examples. As an architectural style, GraphQL is growing in popularity. (See Figure 1.)

Figure 1: Interest in GraphQL has grown significantly since 2015, according to Google Trends.

Figure 1: Interest in GraphQL has grown significantly since 2015, according to Google Trends.

Companies such as Atlassian, Credit Karma, GitHub, Intuit, KLM, Pinterest, Shopify, The New York Times, WordPress and Yelp have made it a prominent part of the way they access data both privately and to the public.

Although the company still doesn't offer a public implementation, GraphQL was created and used first at Facebook. As GraphQL co-creator, Nick Schrock, wrote in 2015,

"It [GraphQL] was invented during the move from Facebook's HTML5-driven mobile applications to purely native applications. It is a query language for graph data that powers the lion's share of interactions in the Facebook Android and iOS applications. Any user of the native iOS or Android app in the last two years has used an app powered by GraphQL."

GraphQL is having a dramatic impact on the way data is created and consumed on the internet. The technology attempts to make exchanging data across a variety of data domains discoverable and efficient; an emerging area of developer need that HTTP-based REST APIs are less equipped to handle. It even tries to fulfill the promise of the Semantic Web. In short, GraphQL might very well be the next step toward unifying data across the internet in a way that is meaningful and machine-readable.

In this opening article of the series, I'm going to present an introduction to the GraphQL — what it is and how it came about. Then in following articles, I'll discuss in a detailed manner the features of GraphQL from an operational perspective. I am going to do this by creating a GraphQL server and commenting on the details for the implementation. After discussing the nuts and bolts of GraphQL, the next article after that is going to provide an in-depth analysis of how GraphQL applies to the Semantic Web.  Finally, I'll report the experiences of a number of companies that have adopted GraphQL Their insights, both good and bad, are invaluable. Learning from the successes and mistakes of others is always a cost-effective way to move forward.

So, let's begin at the beginning. Let's talk about how GraphQL came about and why it's becoming so popular.

 

The Road to GraphQL?

The internet and open standards have fundamentally changed the way that applications access data. Before the introduction of PC based client-server computing, data was stored in mainframe computers that were accessed via dumb terminals. Distributing data among interested parties was accomplished, for the most part by printing reports on paper. (See Figure 2)

Figure 2: Paper based reporting was an early form of data exchange

Figure 2: Paper based reporting was an early form of data exchange

The scope of paper-based mainframe reporting was broad. It included everything from the Accounts Receivable aging reports that businesses depended on to collect monies due in a timely manner to telephone bills sent to the general public. Remember, for every telephone in the US there were month-end bills and payments exchanged between the telephone company and its customers.

Early Methods of Data Exchange

Exchanging data via paper worked, but for obvious reasons it was limited. First and foremost, paper exchange required a lot of human processing to facilitate machine to machine interaction. For every one of the millions of paper bills sent out to customers every month, a clerk at the telephone company had to enter payment information when a bill was paid.

As mainframe computing matured, companies began to exchange information between computers using mutually agreed upon digital formats such as byte-order and word size. Byte-order format is one in which a file containing a series of bytes is exchanged between sender and receiver. Both parties share a specification that defines a field of data according to a byte count. For example, bytes 0-19 can define a field first_name, bytes 20-39 define last_name, bytes 40-49 define date_of_birth, etc. Records will typically be defined by a particular byte value that represents a line break. Defining fields by word size means that a word is defined as an array of bits fixed in size. Then, the size of particular field is determined by the number of words assigned to the particular field.

Regardless of which method was used, parsing data out of files on a byte-by-byte or word-by-word basis was tedious and error-prone. Each sender in the data exchange usually had a proprietary specification that defined the data format that a receiver needed to respect. It was not unusual for receivers to have a shelf full of manuals that described data exchange formats for a variety of vendors. The process was brittle and time-consuming. A better way was needed.

Around 1983 CSV appeared. CSV (comma-separated values) is a standard specification for formatting data as a text file in which a record is defined as a line of data and fields are, as the name implies, separated by a comma. Also, according to the specification, the first line in the file describes the names of the fields to which the lines that follow correspond. Listing 1 below shows a sample of a CSV file that describes a data structure with the field names, id, firstname, lastname, and dob. The lines of text that follow are records according to the those fields name.

"id","firstName","lastName","dob"
"101","David","Bowie","1947-01-08"
"102","Nicholas","Roeg","1928-08-15"
"103","Rip","Torn","1931-02-06"
"104","Candy","Clark","1947-06-20"
"105","James","Dean","1931-02-08"
"106","Buck","Henry","1930-12-09"
"107","Mick","Jagger","1943-07-23"
"108","Susan","Stephen","1931-07-16"
"109","Theresa","Russell","1957-03-20"
"110","James","Fox","1939-05-19"
"111","Anita","Pallenberg","1942-04-06"
"112","John","Bindon","1943-10-04"

Listing 1: Comma separated-values format (CSV) allowed mainframes to exchange structured data electronically

The CSV file format allowed senders and receivers to exchange data according to a common format. However, the physical exchange still proved daunting, particularly when the exchange needed to take place in an asynchronous manner. One solution to make asynchronous data exchange possible was to use an FTP server. (See Figure 3.)

Sending CSV files to FTP servers was an early method for data exchange between mainframe systems.

Figure 3: Sending a CSV file to a FTP server was an early method of data exchange between mainframe systems.

In this process, both sender and receiver share access permissions to a common FTP server. The sender has read/write permissions. The receiver has read permission. The sender copies a file to the FTP server, usually using a predefined file name convention — for example, ar02283.csv.

In that file naming conventions are special to the sender, the filename, ar02283.csv might mean, accounts receivable February 1, 1983, or it could mean, archive record January 2, 1983. In order to understand the file naming convention, a common reference is needed. Thus, while CSV brought a common standard to data exchange, actually doing the exchange was still special to the parties involved. The process was still tedious and error-prone, but it was a significant improvement over counting bytes in binary files. Still, a better way was needed. Fortunately, the internet arrived.

The Rise of Data Driven HTML

The language that's responsible for the structure and layout of web pages — Hypertext Markup Language or HTML — is old hat by now (in fact, we're onto version 5). We've all become accustomed to being able to read the information on a website as easily as our grandparents read newspapers. But, when it first appeared, HTML was a game-changer. Before HTML came along, data was published using proprietary reporting software such as Oracle RPT or Crystal Reports. There was no open publishing standard. HTML was the open standard that provided the versatility and power to publish information to web pages. HTML was the transformational technology that made accessing information available on the World Wide Web nothing more than a mouse click away.

The early history of HTML was about static data. Web developers typed out hard coded information into static files that were decorated with HTML markup. The web pages were stored on web servers accessed via web browsers. Static web pages were powerful, but they didn't provide easy access to the volumes of data stored in the hundreds of thousands of databases over the planet. Again, something more was needed. That something more, in addition to programming to the Common Gateway Interface (CGI) using a language such as Perl, were dynamic web page technologies such as PHP (See Listing 2), Java Server Pages (JSP), and Active Server Pages (ASP).

<?php
$servername = "xxxx.imbob.org'";
$username = "username";
$password = "password";
$dbname = "actorDB";
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
//get the data
$sql = "SELECT first_name last_name FROM MyGuests";
$actors = $conn->query($sql);
?>
<html>
<head>
   <title>Actors</title>
</head>
<body>
<h1>Actors</h1>
<ul>
   <!-- iterate through the actors resultset and populate the web page -->
<?php foreach ( $actors as $actor ) { ?>
    <li>
        <a href="<?php echo $actor->wiki_url ?>"><?php echo $actor->first_name?> <?php echo $actor->last_name?> 
    </li>
<?php } ?>
</ul>
</body>
</html>

Listing 2: Embedding data in HTML on the server side provided a way to easily publish machine readable information on the web.

The introduction of dynamic web page technologies made it so that web developers could write server-side programs that accessed data in databases and decorated it with HTML. Whereas in the past, report writing was based on proprietary technology, now with dynamic HTML technologies, data from a database could be published using a common rendering technology — HTML — and accessed just as easily using a common data access protocol — HTTP.

Connecting Web Data Using Hypermedia

HTML also provided a feature that was never available before in any data publishing paradigm: the ability to link data in a web page to data and media in web pages in the same domain and other domains. This feature is hypertext. Effectively, hypertext put "browsing" in the web browser. A piece of content is marked like so:

<a href="URL_TO_OTHER_DATA>some data

Then, a human reader can click on the link to go to the other data. (See Figure 4.)

Figure 4: HTML hyperlinks make information in various media formats accessible nonlinearly

Figure 4: HTML hyperlinks make information in multiple media formats accessible in a nonlinear manner

Hypertext embedded in web pages that are rendered by a web browser with complete access to the internet was a realization of a pre-Web prediction made by Bill Gates back in 1989. Information was now at your fingertips. Hypertext also made information retrieval nonlinear. Using hypertext, humans could read the information on a web page and follow that information anywhere at any time. It was a profound transformation to the way humans absorbed information. In addition, hyperlink technology was equally transformational for digital applications. For example, when the entity reading the link on a web page is a machine-driven SEO engine scouring the internet, hyperlinks provide the way for those machines to go find the "next" piece of data in the information chain, even if it was published on another domain. Thus, machines could now crawl the continuously growing amount of data being published to the internet. However, in order to make the data useful, it needed to be made relatable. Defining relationships between data points on the internet was the next challenge to be met as information technology evolved toward GraphQL.

Defining Relationships Across the Web

Using forward-pointing hyperlinks to continuously traverse and connect data across the internet was a significant breakthrough in information technology. Yet, in order to turn the connected data into useful information, the relationships between the various data points need to be well defined and discoverable. This fact was not lost on the World Wide Web Consortium (W3C), the standards-setting body of the World Wide Web. Thus, it built a relationship definition parameter — rel — into the HTML specification. (In fact, in years to come, the W3C expanded this relationship definition to include the standards set forth by the Semantic Web, which we'll discuss extensively in Part 4 of this series.)

Listing 3 below shows how the rel parameter can be used to define relationships in HTML. The HTML, which is taken from the web page shown above in Figure 2., contains a list of people who are connected to the web page's subject, Nicholas Roeg. The list of people is rendered as an unordered list in HTML. (The unordered list begins at line 11 and ends at line 19 in Listing 3.)

1:   <html>
2:   <head>
3:   <title>Profile</title>
4:   </head>
5:   <body>
6:    <div>Nicholas Roeg</div>
7:    <div>1928-08-15</div>
8:    <div>
9:      <div>Connections</div>
10:      <div>
11:        <ul>
12:          <li><a rel="knows workedWith likes" href="https://en.wikipedia.org/wiki/David_Bowie">David Bowie</a></li>
13:          <li><a rel="knows workedWith" href="https://en.wikipedia.org/wiki/Rip_Torn">Rip Torn</a></li>
14:          <li><a rel="knows workedWith" href="https://en.wikipedia.org/wiki/Candy_Clark">Candy Clark</a></li>
15:          <li><a rel="knows workedWith likes" href="https://en.wikipedia.org/wiki/Buck_Henry">Buck Henry</a></li>
16:           <li><a rel="knows workedWith" href="https://en.wikipedia.org/wiki/Mick_Jagger">Mick Jagger</a></li>
17:          <li><a rel="knows marriedTo" href="https://en.wikipedia.org/wiki/Susan_Stephen">Susan Stephen</a></li>
18:          <li><a rel="knows workedWith marriedTo" href="https://en.wikipedia.org/wiki/Theresa_Russell">Theresa Russell</a></li>
19:        </ul>
20:      </div>
21:     </div>
22:    </div>
23:    </body>
24:  </html>

Listing 3: The HTML rel attribute can be used to describe relationships between a parent document and a linked document.

Each item in the unordered list is tagged with a link to the person's Wikipedia page in Wikipedia. If a user clicks on one of those links, the browser goes to the Wikipedia URL defined by the link's href tag. By now, this is a common technique that even a child using a Google Document can accomplish.

However, the anchor tags (<a>) in Listing 3 contain an important piece of additional information that defines how the target link relates to the hosting page. Take a look at the HTML shown at line 12 of Listing 3:

<li><a rel="knows workedWith likes" href="https://en.wikipedia.org/wiki/David_Bowie">David Bowie</a></li>

Notice the rel="knows workedWith likes" attribute highlighted in bold. The rel attribute is a part of the HTML specification that can be used to define how a web page relates to the links it targets. In this case, the attribute indicates that the subject of the link, David Bowie, has three defined relationships to the hosting web page. These relationships are, knows, workedWith and likes. Thus, by using the rel tag, the web page is informing inspecting entities (ie: a machine that crawls the page) that David Bowie knows Nicholas Roeg, that David Bowie has worked with Nicholas Roed and that David Bowie likes Nicholas Roeg. With the "rel" parameter, the link now provides a way to not only navigate to related information, but also a way to understand how a web page is related to the information it links to.

The good news is that the relationships that various people have with the web page's subject, Nicholas Roeg, is discoverable. But, there is still a problem. Without a common point of reference about the meaning of the terms knows, workedWith and likes, there's really no way to understand the exact definition of the relationships. Does Nicholas Roeg know David Bowie because he bought one of the artist's albums? Or, have they met in person? Without a reference defining a common vocabulary, aka an ontology, there's no way to be sure.

Creating such a common vocabulary comes later on with the introduction of XML namespaces. (Ontologies are another subject that will be covered in Part 3 of this series.) Still, the rel tag was an important beginning for unifying the web. It not only provided a way for humans to understand data on the internet but for machines as well. In fact, as the amount of information on the internet continued to grow, so too did machine ingestion of that data, so much so, that HTML became exhausted.

Outside of web browsers, machine-driven applications don't really care that much about human readable formats. These applications want data in machine-readable formats that were easy to consume. The proliferation of machine activity on the internet was the impetus behind the rise to the machine-readable, data formats of XML and later on JSON.

The Introduction of Common Formats

HTML is OK for human consumption, but more elegant data formats are required to make machine consumption more efficient. Hence, XML and JSON. XML (Extensible Markup Language) was first proposed as a working draft by the W3C dated November 14, 1996. Since that time, the specification has gone through a number of revisions. The specification is well known today and still used in business and academia.

Listing 4 is an XML sample that could represent the list of persons described in the web page and HTML shown above in Figure 4 and Listing 4, respectively.

<persons>
  <person id="101" firstName="David" lastName="Bowie" dob="1947-01-08" />
  <person id="102" firstName="Nicholas" lastName="Roeg" dob="1928-08-15" />
  <person id="103" firstName="Rip" lastName="Torn" dob="1931-02-06" />
  <person id="104" firstName="Candy" lastName="Clark" dob="1947-06-20" />
  <person id="105" firstName="Mick" lastName="Jagger" dob="1943-07-23" />
  <person id="106" firstName="Buck" lastName="Henry" dob="1930-12-09" />
</persons>

Listing 4: XML is a standard way to format data for publication on the internet

XML is similar in syntax to HTML. It structures data with user-defined opening and closing tags. And, an opening tag can contain user-defined attributes that can be used to describe fields within the structure.

JSON, (Javascript Object Notation) is a way to format data following the standard used by Javascript to describe objects and arrays. Javascript and the web are inextricably linked due to Javascript's exclusive role as the standard programming language for automating tasks within web browsers which in turn was a launchpad for JSON's popularity with developers. JSON's star rose even further as Javascript found its place next to PHP, Python, and Java as a server-side programing language (officially known as Node.js). Listing 5 below is a sample of JSON that describes all the information described in the web page and HTML shown above in Figure 4 and Listing 4, respectively. Listing 5, also compares the JSON to an equivalent expression in XML.

Representing a movie in JSON
{
                                                                                                                                                   "id": 4001,
                                                                                                                                                  "title": "The Man Who Fell to Earth",
                                                                                                                                                   "releaseDate": "1976-04-18",
                                                                                                                                                  "director":{"id": 102, "firstName": "Nicholas", "lastName": "Roeg", "dob": "1928-08-15"},
                                                                                                                                                  "actors" : [
                                                                                                                                                     {"id": 101, "firstName": "David", "lastName": "Bowie", "dob": "1947-01-08"},
                                                                                                                                                    {"id": 103, "firstName": "Rip", "lastName": "Torn", "dob": "1931-02-06"},
                                                                                                                                                    {"id": 104, "firstName": "Candy", "lastName": "Clark", "dob": "1947-06-20"},
                                                                                                                                                    {"id": 106, "firstName": "Buck", "lastName": "Henry", "dob": "1930-12-09"}
                                                                                                                                                    ]
                                                                                                                                                }
Representing a movie in XML
<movie>
                                                                                                                                                  <id>4001</id>
                                                                                                                                                  <title>The Man Who Fell to Earth</title>
                                                                                                                                                  <releaseDate>1976-04-18</releaseDate>
                                                                                                                                                  <director>
                                                                                                                                                    <id>102</id>
                                                                                                                                                    <firstName>Nicholas</firstName>
                                                                                                                                                    <lastName>Roeg</lastName>
                                                                                                                                                    <dob>1928-08-15</dob>
                                                                                                                                                  </director>
                                                                                                                                                  <actors>
                                                                                                                                                    <actor>
                                                                                                                                                      <id>101</id>
                                                                                                                                                      <firstName>David</firstName>
                                                                                                                                                      <lastName>Bowie</lastName>
                                                                                                                                                      <dob>1947-01-08</dob>
                                                                                                                                                    </actor>
                                                                                                                                                    <actor>
                                                                                                                                                      <id>103</id>
                                                                                                                                                      <firstName>Rip</firstName>
                                                                                                                                                      <lastName>Torn</lastName>
                                                                                                                                                      <dob>1931-02-06</dob>
                                                                                                                                                    </actor>
                                                                                                                                                    <actor>
                                                                                                                                                      <id>104</id>
                                                                                                                                                      <firstName>Candy</firstName>
                                                                                                                                                      <lastName>Clark</lastName>
                                                                                                                                                      <dob>1947-06-20</dob>
                                                                                                                                                    </actor>
                                                                                                                                                    <actor>
                                                                                                                                                      <id>106</id>
                                                                                                                                                      <firstName>Buck</firstName>
                                                                                                                                                      <lastName>Henry</lastName>
                                                                                                                                                      <dob>1930-12-09</dob>
                                                                                                                                                    </actor>
                                                                                                                                                  </actors>
                                                                                                                                                </movie>

Listing 5: Compared to XML, JSON is a more concise data format data for publishing information on the internet

Notice please that while both XML and JSON provide a means for structuring data in a way that's agnostic of any technology or vendor, JSON has the benefit of being a more concise format, as demonstrated in Listing 3, above. Thus, it's gaining broader acceptance among business and academia. While there is some use of XML presently, JSON is becoming the preferred method for structuring data using a text-based format. In fact, as you'll see when we start the look at using GraphQL to work with graph data, the information will be retrieved in JSON format.

Mobile Devices, APIs and REST

The days of a single application working with a single data source are over. Today, it's typical for an application to aggregate and transform data from a variety of sources into datasets that are special to the need at hand. And, as mobile devices become increasingly more powerful, more aggregation and transformation activity is happening directly on the client (ie: a mobile app running on a smartphone or a Javascript-driven web app running in a browser). A result is that proprietary data access methods and formats are being abandoned in favor of generic open data access technologies and common data formats. In other words, wherein the past applications used a proprietary flavor of SQL to work with data stored in a proprietary database such as Oracle, DB2 or SQL Server, today, application developers prefer to use generic, internet-based APIs that return data in agnostic text-based formats such as XML and JSON or that are serialized in an open-source binary format such as protocol buffers. This level of abstraction reduces development time and increases the ability of an application to scale. API based development is a fundamental transformation in the way software is made.

Before GraphQL came along, most popular APIs used an adaptation of REST. REST, which is an acronym for Representational State Transfer and is an architectural style defined by computer scientist Roy Fielding in a doctoral dissertation published in 2000. REST is a comprehensive approach to software design that utilizes the basic features of the web's HTTP protocol to work with applications. This reliance on HTTP is why REST APIs are often called Web APIs and vice versa (even though not all HTTP-based APIs adhere to the fundamentals of REST). In REST, an application represents itself as URIs within a domain that are accessed using the standard HTTP methods, GET, HEAD, POST, PUT, PATCH, DELETE, CONNECT, OPTIONS and TRACE to perform actions upon the application. These methods, also known as "verbs," are identical to the those used when a web browser issues a request to a web site. The application responds to these requests with data, status codes and other information contained in the response header. Also, an application can return URIs within the responses that describe subsequent actions available to execute. For example, the following URI is an API published by the domain Open Library. (Open Library provides books online for free.)

The URI describes a resource, books. Also, the URI has a query string that indicates a particular book resource, according to ISBN number.
https://openlibrary.org/api/books?bibkeys=ISBN:0451526538

Listing 6 below, illustrates the response to a request made against the URI shown above using the HTTP GET method.

The response contains information about the book resource in JSON format. Notice that the response returns not only the bib_key field containing the ISBN number but also, the field, preview with a value indicating that noview is available. Also, the response contains three other fields that have URIs as values. These URIs indicate the next steps possible in the workflow for this particular application.

{
  "ISBN:0451526538": {
    "bib_key": "ISBN:0451526538",
    "preview": "noview",
    "thumbnail_url": "https://covers.openlibrary.org/b/id/295577-S.jpg",
    "preview_url": "https://openlibrary.org/books/OL1017798M/The_adventures_of_Tom_Sawyer",
    "info_url": "https://openlibrary.org/books/OL1017798M/The_adventures_of_Tom_Sawyer"
  }
}

Listing 6: A response from a RESTful API that contains URIs that describe subsequent actions actions for viewing a thumbnail image or previewing data or general information about the book, The Adventures of Tom Sawyer.

The client application can call the URI associated with the field, thumbnail_url to view a thumbnail image of the book. The application can call the preview_url to get a preview of the book or the it can call info_url to get more information about the book.

As you can see, REST is using the concepts of hypertext and hypermedia to indicate the next possible actions and data points available in the application's workflow and information chain. Using hypertext and hypermedia to describe options by which to continue to view or alter an application's state per a given response is a powerful feature of REST. In order for an API to fully support REST, it needs to provide forward pointing references in a response. APIs that allow clients to do nothing more than perform Create, Read, Update and Delete (aka "CRUD") actions on resources are considered to be only RESTful (ie: they bear some but not all characteristics of REST). It's a subtle distinction, but an important one nonetheless.

REST and RESTful APIs have transformed the way developers create applications. APIs add a lot of elegance and efficiency to software design. However, RESTful APIs have a few drawbacks; the most prominent are that they tend to create a lot of round trip traffic (due to the multiple requests they often make to advance an application's state), and they are not easily recursive. For example, in the books API shown above, once the request for a particular book resource is returned another trip back to the network is necessary to get follow up information such as the books thumbnail or additional information. Having to make multiple trips to the network to get the complete information change gives way to the recursion problem.

There's no easy way to get an REST or RESTful API go return portions of a given information chain recursively. This means that if, as in the books API, I want to get and show not only the additional book information but also the information within the book information, it can't be done in one declarative statement. I have to go back to the network for the addition information.

This problem of network round trips and recursion were ones that afflicted Facebook as it tried to make its News Feed feature more performant. The first thing that the client applications did was load in the News Feed. Then, if the user wanted to view the comments associated with a particular post in that feed, or find out more about the people making those comments, the client application's only option was to make trips back to the network. The process was time-consuming in terms of client execution. Also, the programming it took to implement the behavior was brittle. Making a change was hard. The way they addressed these and other problems was to create GraphQL.

 

GraphQL Redefines Data Access on the Internet

GraphQL was created by Facebook to address a very specific problem: how to control its news feed in native mobile applications. The person responsible for the Facebook News Feed was Lee Byron, one of the co-creators of GraphQL.

When he was interviewed for this series of articles, Byron told ProgrammableWeb that he and his team at Facebook worked for years to optimize the News Feed in its various iterations (Byron has since left Facebook to lead web engineering at the commission-free investment startup Robinhood). So had other teams within Facebook. Early versions of the News Feed were built on an internal RESTful API developed around 2009. It was a feature developed for a group of third-party companies wanting to work with the New Feed data. At the time the API was little known inside Facebook. Byron got wind of the API in 2012 while reviewing some refactoring work his developers were doing to improve the News Feed code. While the API provided some utility, Byron noticed that large segments of data were missing from the feed, data such as comments on a post or aggregations of data emitted among friends. Byron realized the API had two significant drawbacks. One was network latency. According to Byron:

"REST really wants to have one model per URL. Recursion is just really difficult to correctly model in that framing, especially when, in order to resolve any of the links between these things, you need to go back to the network. And here we're talking about relatively early days of the smartphone world where the vast majority of people are still on 3G, 4G isn't even a thing yet, let alone LTE. So network is absolutely the bottleneck."

The other constraint was recursion. The API's recursion mechanisms made it difficult to get additional information about a particular data point on demand, such as viewing a list of friends liking a particular story. Byron and his team began to look for a new way to approach the publication of News Feed data.

At the time Facebook had released a new technology, FQL (Facebook Query Language). which was a derivation of SQL. Unlike SQL which typically interacts directly with the given database engine, FQL was designed to query against a code abstraction layer that represented News Feed data. This code abstraction layer linked various pieces of Facebook's application tier together to fulfill FQL queries.

FQL addressed the network bottleneck issue, but it fell short addressing the recursion problem. Writing recursive FQL queries was difficult. Development teams using FQL needed to have at least one member who had a deep understanding of its working in order make server-side operations performant. There weren't a lot of people on staff with this type of talent. Faced with a limited number of developers who could do the FQL optimization work and the growing complexity and volume of the backend queries created to support the demands of the News Feed, Byron decided to look for a better way. That better way required that he and his fellow engineers change their thinking about data structures. They needed to move away from conceptualizing datasets as tables toward a different type of data structure: the object graph. This change in thinking was critical to the emergence of GraphQL.

From Data Tables to the Object Graph

Although FQL allowed front-end developers to get at Facebook's News Feed data faster, it didn't solve a fundamental architectural round-peg, square-hole problem. Speaking of GraphQL co-creator Nick Schrock's assessment of FQL, Byron told ProgrammableWeb:

"As it turns out that Nick [Schrock, creator of FQL] who had been working on FQL was also frustrated with FQL, but for very different reasons. He felt that FQL was squishing a square peg through a round hole. On the server side of Facebook, the way all the abstractions are set up is to think about data in terms of graphs. So there's objects that relate to other objects with one or one-to-many [relationships]. And everything is written in a very graphy sort of language. But FQL being a sort of variant of SQL wants to think about everything as tables and join tables and joins. And those two ideas in Nick's opinion didn't fit very well together. And he felt that while what he had built ended up working, it felt very hacky.

Both the client side and server side teams were uncomfortable working with FQL. The client-side developers and server-side developers talked about data in terms of an object graph, yet FQL was fundamentally tabular in concept. As Byron reported to ProgrammableWeb,

"You've got a square-peg, round problem on the server and a round-peg, square-hole problem on the client, so we thought, 'hey we've got to get rid of this table abstraction all together and get back to round-peg, round hole.'"

Thus, emerged the idea for GraphQL. GraphQL was built from the ground up by its co-creators Lee Byron, Dan Schafer, and Nick Schrock to be an API and query language for object graphs.

Figure 5: An object graph structures data according to nodes and edges

Figure 5: An object graph structures data according to nodes and edges

GraphQL is intended to be used to create APIs that support models that can be retrieved by a single request from the server. And, GraphQL is defined to support declarative recursion from within a single query. Declarative recursion means that developers can create a single query that effectively says "show me a list of movies according to title, releaseDate, directors and actors and show me who each director knows and likes", (See Figure 6) The developer can delve deeper into the graph if so desired. For example, the query can be extended to recurse further down the graph to ask for the likes and knows of the people that the director likes and knows, as so on.

Figure 6: GraphQL provides API access to entities and their relationships using continuous recursion

Figure 6: GraphQL provides API access to entities and their relationships using continuous recursion

Fulfilling the query is done behind the scenes. The developer doesn't have to do any fancy joins as are typical when working with tables to a relational database. The object graph is the building block upon which queries are executed.

The important thing to understand about GraphQL is that it's intended to provide a way to retrieve structured, recursive data within the constraint of a single request to the server. In other words, once the initial recursive declaration is made, no other action needs to take place. Also, another important thing to understand about GraphQL is that it's only a specification, just as SQL is only a specification. GraphQL itself is not an API nor is it a product. Implementing some technology to support that specification is another activity altogether. The specification is the mechanism that allows anybody to work with a GraphQL API regardless of the underlying technology and language used to publish data through the API. GraphQL is platform agnostic and there are in fact several implementations for a variety of platforms. However, in order to work with a GraphQL API, a fundamental understanding of the specification is required.

Understanding the GraphQL Specification

As mentioned above, GraphQL is an open source specification for implementing GraphQL-compliant API in a specific technological framework. For example, the implementation used in this series is Apollo Server, which is powered by node.js. There are also implementations in C#/.NET, Golang, Ruby, Java and Python, among others.

The GraphQL specification is distinctive in 6 ways:

  • The query language itself is special.
  • The specification requires the use of custom object types to define data models.
  • GraphQL requires that an API support implementations of the following operations, Query, Mutation and Subscriptions.
  • The specification supports abstract types such as interfaces and unions
  • The specification support introspection
  • The specification supports publish-and-subscribe messaging. Within ProgrammableWeb's API Directory, such publish and subscribe API fall under a larger umbrella of push/streaming APIs; APIs that let clients know when there's new information (as opposed to the client having to constantly check or "poll" an API for updates).

The following sections describe each feature in detail.

The GraphQL Query Language

The GraphQL query syntax is special. It's a declarative format that looks something like a cross between JSON and Python. The query language supports the curly bracket syntax to define a set of fields within an object (aka entity). But, unlike the way JSON uses commas to delimit a field, a GraphQL query uses line breaks. Listing 5 below shows an example of a GraphQL query for a distinct Movie and the result of that query.

GraphQL query
{
                                                                                                                                                  movie(id: "6fceee97-6b03-4758-a429-2d5b6746e24e"){
                                                                                                                                                    title
                                                                                                                                                    releaseDate
                                                                                                                                                    directors{
                                                                                                                                                      firstName
                                                                                                                                                      lastName
                                                                                                                                                      dob
                                                                                                                                                    }
                                                                                                                                                    actors{
                                                                                                                                                      firstName
                                                                                                                                                      lastName
                                                                                                                                                      roles{
                                                                                                                                                          character
                                                                                                                                                      }
                                                                                                                                                    }
                                                                                                                                                  }
                                                                                                                                                }
Result
{
                                                                                                                                                  "data": {
                                                                                                                                                    "movie": {
                                                                                                                                                      "title": "The Man Who Fell to Earth",
                                                                                                                                                      "releaseDate": "1976-04-18",
                                                                                                                                                      "directors": [
                                                                                                                                                        {
                                                                                                                                                          "firstName": "Nicholas",
                                                                                                                                                          "lastName": "Roeg",
                                                                                                                                                          "dob": "1928-08-15"
                                                                                                                                                        }
                                                                                                                                                      ],
                                                                                                                                                      "actors": [
                                                                                                                                                        {
                                                                                                                                                          "firstName": "David",
                                                                                                                                                          "lastName": "Bowie",
                                                                                                                                                          "roles": [
                                                                                                                                                            {
                                                                                                                                                              "character": "Thomas Jerome Newton"
                                                                                                                                                            }
                                                                                                                                                          ]
                                                                                                                                                        },
                                                                                                                                                        {
                                                                                                                                                          "firstName": "Rip",
                                                                                                                                                          "lastName": "Torn",
                                                                                                                                                          "roles": [
                                                                                                                                                            {
                                                                                                                                                              "character": "Nathan Bryce"
                                                                                                                                                            }
                                                                                                                                                          ]
                                                                                                                                                        },
                                                                                                                                                        {
                                                                                                                                                          "firstName": "Candy",
                                                                                                                                                          "lastName": "Clark",
                                                                                                                                                          "roles": [
                                                                                                                                                            {
                                                                                                                                                              "character": "Mary-Lou"
                                                                                                                                                            }
                                                                                                                                                          ]
                                                                                                                                                        },
                                                                                                                                                        {
                                                                                                                                                          "firstName": "Buck",
                                                                                                                                                          "lastName": "Henry",
                                                                                                                                                          "roles": [
                                                                                                                                                            {
                                                                                                                                                              "character": "Oliver Farnsworth"
                                                                                                                                                            }
                                                                                                                                                          ]
                                                                                                                                                        }
                                                                                                                                                      ]
                                                                                                                                                    }
                                                                                                                                                  }
                                                                                                                                                }

Listing 7: The GraphQL query on the top defines a result shown on the bottom

The meaning behind the query in Listing 7 is as follows: "Show me information about a movie according to the id, 6fceee97-6b03-4758-a429-2d5b6746e24e. The information to return is the movie title and release date, Also show me the directors of the movie, according to firstName, lastName, and dob. And, return the collection of actors in the movie according to the firstName, lastName and the role or roles the actor played."

The result of the query defined at the top of Listing 7 is shown at the bottom of the listing.
 

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


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.

After a client registers a subscription, it receives API messages when predefined events 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
Query Results
"""
                                                                                                                                                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
                                                                                                                                                        }
                                                                                                                                                      } 
                                                                                                                                                    }
                                                                                                                                                  }
                                                                                                                                                }
{
                                                                                                                                                  "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