How I learned GraphQL is the better REST
About me
Hi. I'm Joel, a visionary who has learned to wield technology for the betterment of our collective ideas. I like to travel, explore, adventure, hike, climb, write, build, share, and learn how to get the best ideas to market. I'm a blend of visionary, integrator, and marketer whose true direction is North. I ride the wave of full stack architect and full stack marketer on the daily ... finding the best ways to get us from here to where we know we want to be.
Why I wanted to learn GraphQL
I have always been known to and for being on the bleeding edge of advancement, either in technology, philosophy, or elsewhere... it's an essential gift of being a visionary. So for someone who starts a lot of new projects on stacks that become growing businesses that need to scale the entire toolchain, including the methods of securing and transporting data between endpoints of any type (IoT, mobile, web, API, chat, bot, etc); and also knowing that real time communication is a request often made by app owners and product designers; I set out to listen to what the vision community of technology futurists were saying and what I heard was this over and over; REST is DEAD.
Well - yes, it should be. It can be rather difficult to secure, optimize, and tune to efficiency at scale (especially at the developer experience), which most applications end up requiring beyond the two year mark. All software bloats, and I need a way to give app developers a better workflow to building future proof stacks for their business, so they can continue to focus on the thing that truly matters most: creating value for customers and/or users.
Enter GraphQL - where building serverless, API driven full stack applications is like making pancakes - mix, heat, and eat - with no mess.
GraphQL is a new API standard that provides a more efficient, powerful and flexible alternative to REST. It was developed and open-sourced by Facebook and is now maintained by a large community of companies and individuals from all over the world.
APIs have become ubiquitous components of software infrastructures. In short, an API defines how a client can load data from a server.
At its core, GraphQL enables declarative data fetching where a client can specify exactly what data it needs from an API. Instead of multiple endpoints that return fixed data structures, a GraphQL server only exposes a single endpoint and responds with precisely the data a client asked for.
GraphQL - A Query Language for APIs
Most applications today have the need to fetch data from a server where that data is stored in a database. It’s the responsibility of the API to provide an interface to the stored data that fits an application’s needs.
GraphQL is often confused with being a database technology. This is a misconception, GraphQL is a query language for APIs - not databases. In that sense it's database agnostic and effectively can be used in any context where an API is used.
A more efficient Alternative to REST
💡 Learn more about the top reasons to use GraphQL in this blog post.
REST has been a popular way to expose data from a server. When the concept of REST was developed, client applications were relatively simple and the development pace wasn’t nearly where it is today. REST thus was a good fit for many applications. However, the API landscape has radically changed over the last couple of years. In particular, there are three factors that have been challenging the way APIs are designed:
1. Increased mobile usage creates need for efficient data loading
Increased mobile usage, low-powered devices and sloppy networks were the initial reasons why Facebook developed GraphQL. GraphQL minimizes the amount of data that needs to be transferred over the network and thus majorly improves applications operating under these conditions.
2. Variety of different frontend frameworks and platforms
The heterogeneous landscape of frontend frameworks and platforms that run client applications makes it difficult to build and maintain one API that would fit the requirements of all. With GraphQL, each client can access precisely the data it needs.
3. Fast development & expectation for rapid feature development
Continuous deployment has become a standard for many companies, rapid iterations and frequent product updates are indispensable. With REST APIs, the way data is exposed by the server often needs to be modified to account for specific requirements and design changes on the client-side. This hinders fast development practices and product iterations.
History, Context & Adoption
GraphQL is not only for React Developers
Facebook started using GraphQL in 2012 in their native mobile apps. Interestingly though, GraphQL has mostly been picked up to be used in the context of web technologies and has gained only little traction in the native mobile space.
The first time Facebook publicly spoke about GraphQL was at React.js Conf 2015 and shortly after announced their plans to open source it. Because Facebook always used to speak about GraphQL in the context of React, it took a while for non-React developers to understand that GraphQL was by no means a technology that was limited to usage with React.
Dan Schafer & Jing Chen publicly introduce GraphQL React.JS Conf 2015. Watch video.
A rapidly growing Community
In fact, GraphQL is a technology that can be used everywhere a client communicates with an API. Interestingly, other companies like Netflix or Coursera were working on comparable ideas to make API interactions more efficient. Coursera envisioned a similar technology to let a client specify its data requirements and Netflix even open-sourced their solution called Falcor. After GraphQL was open-sourced, Coursera completely cancelled their own efforts and hopped on the GraphQL train.
Today, GraphQL is used in production by lots of different companies such as GitHub, Twitter, Yelp and Shopify - just to name only a few.
Despite being such a young technology, GraphQL has already been widely adopted. Learn who else is using GraphQL in production.
There are entire conferences dedicated to GraphQL such as GraphQL Summit or GraphQL Europe and more resources like the GraphQL Radio podcast and GraphQL Weekly newsletter.
How I approached learning GraphQL
Over the past decade, REST has become the standard (yet a fuzzy one) for designing web APIs. It offers some great ideas, such as stateless servers and structured access to resources. However, REST APIs have shown to be too inflexible to keep up with the rapidly changing requirements of the clients that access them.
GraphQL was developed to cope with the need for more flexibility and efficiency! It solves many of the shortcomings and inefficiencies that developers experience when interacting with REST APIs.
To illustrate the major differences between REST and GraphQL when it comes to fetching data from an API, let's consider a simple but typical scenario:
A content application which needs to display the titles of posts of a specific user
The same screen also displays the names of the last 3 followers of that user. How would that situation be solved with REST and GraphQL?
Data Fetching with REST vs GraphQL
With a REST API, we would typically gather the data by accessing multiple endpoints. In the example, these could be /users/<id>
endpoint to fetch the initial user data. Secondly, there's likely to be a /users/<id>/posts
endpoint that returns all the posts for a user. The third endpoint will then be the /users/<id>/followers
that returns a list of followers per user.
With REST, we have to make three requests to different endpoints to fetch the required data. We're also overfetching since the endpoints return additional information that's not needed.
In GraphQL on the other hand, we'd simply send a single query to the GraphQL server that includes the concrete data requirements. The server then responds with a JSON object where these requirements are fulfilled.
Using GraphQL, the client can specify exactly the data it needs in a query. Notice that the structure of the server's response follows precisely the nested structure defined in the query.
No more Over- and Underfetching
One of the most common problems with REST is that of over- and underfetching. This happens because the only way for a client to download data is by hitting endpoints that return fixed data structures. It's very difficult to design the API in a way that it's able to provide clients with their exact data needs.
"Think in graphs, not endpoints." Lessons From 4 Years of GraphQL by Lee Byron, GraphQL Co-Inventor.
Overfetching: Downloading superfluous data
Overfetching means that a client downloads more information than is actually required in the app. Imagine for example a screen that needs to display a list of users only with their names. In a REST API, this app would usually hit the /users
endpoint and receive a JSON array with user data. This response however might contain more info about the users that are returned, e.g. their birthdays or addresses - information that is useless for the client because it only needs to display the users' names.
Underfetching and the n+1 problem
Another issue is underfetching and the n+1-requests problem. Underfetching generally means that a specific endpoint doesn’t provide enough of the required information. The client will have to make additional requests to fetch everything it needs. This can escalate to a situation where a client needs to first download a list of elements, but then needs to make one additional request per element to fetch the required data.
As an example, consider the same app would also need to display the last three followers per user. The API provides the additional endpoint /users/<user-id>/followers
. In order to be able to display the required information, the app will have to make one request to the /users
endpoint and then hit the /users/<user-id>/followers
endpoint for each user.
Rapid Product Iterations on the Frontend
A common pattern with REST APIs is to structure the endpoints according to the views that we have inside our app. This is handy since it allows for the client to get all required information for a particular view by simply accessing the corresponding endpoint.
The major drawback of this approach is that it doesn’t allow for rapid iterations on the frontend. With every change that is made to the UI, there is a high risk that now there is more (or less) data required than before. Consequently, the backend needs to be adjusted as well to account for the new data needs. This kills productivity and notably slows down the ability to incorporate user feedback into a product.
With GraphQL, this problem is solved. Thanks to the flexible nature of GraphQL, changes on the client-side can be made without any extra work on the server. Since clients can specify their exact data requirements, no backend engineer needs to make adjustments when the design and data needs on the frontend change.
Insightful Analytics on the Backend
GraphQL allows us to have fine-grained insights about the data that’s requested on the backend. As each client specifies exactly what information it’s interested in, it is possible to gain a deep understanding of how the available data is being used. This can for example help in evolving an API and deprecating specific fields that are not requested by any clients any more.
With GraphQL, we can also do low-level performance monitoring of the requests that are processed by our server. GraphQL uses the concept of resolver functions to collect the data that’s requested by a client. Instrumenting and measuring performance of these resolvers provides crucial insights about bottlenecks in our system.
Benefits of a Schema & Type System
GraphQL uses a strong type system to define the capabilities of an API. All the types that are exposed in an API are written down in a schema using the GraphQL Schema Definition Language (SDL). This schema serves as the contract between the client and the server to define how a client can access the data.
Once the schema is defined, the teams working on frontend and backends can do their work without further communication since they both are aware of the definite structure of the data that's sent over the network.
Frontend teams can easily test their applications by mocking the required data structures. Once the server is ready, the switch can be flipped for the client apps to load the data from the actual API.
Challenges I faced
The challenge in building an API these days is the fundamental language constructs of GraphQL. This includes the first glimpse we take at the syntax for defining types as well as sending queries and mutations...it's totally unRESTful and very remiss of the days of running sql
queries to fetch data.
So to help us get over the initial hump, here's how we break down core concepts of GraphQL where at the end we'll start a project.
The Schema Definition Language (SDL)
GraphQL has its own type system that’s used to define the schema of an API. The syntax for writing schemas is called Schema Definition Language (SDL).
Here is an example how we can use the SDL to define a simple type called Person
:
type Person {
name: String!
age: Int!
}
This type has two fields, they’re called name
and age
and are respectively of type String
and Int
. The !
following the type means that this field is required.
It’s also possible to express relationships between types. In the example of a blogging application, a Person
could be associated with a Post
:
type Post {
title: String!
author: Person!
}
Conversely, the other end of the relationship needs to be placed on the Person
type:
type Person {
name: String!
age: Int!
posts: [Post!]!
}
Note that we just created a one-to-many-relationship between Person
and Post
since the posts
field on Person
is actually an array of posts.
Fetching Data with Queries
When working with REST APIs, data is loaded from specific endpoints. Each endpoint has a clearly defined structure of the information that it returns. This means that the data requirements of a client are effectively encoded in the URL that it connects to.
The approach that’s taken in GraphQL is radically different. Instead of having multiple endpoints that return fixed data structures, GraphQL APIs typically only expose a single endpoint. This works because the structure of the data that’s returned is not fixed. Instead, it’s completely flexible and lets the client decide what data is actually needed.
That means that the client needs to send more information to the server to express its data needs - this information is called a query.
Basic Queries
Let’s take a look at an example query that a client could send to a server:
{
allPersons {
name
}
}
The allPersons
field in this query is called the root field of the query. Everything that follows the root field, is called the payload of the query. The only field that's specified in this query's payload is name
.
This query would return a list of all persons currently stored in the database. Here’s an example response:
{
"allPersons": [
"Johnny" },
{ "name": "Sarah" },
{ "name": "Alice" }
]
}
Notice that each person only has the name
in the response, but the age
is not returned by the server. That’s exactly because name
was the only field that was specified in the query.
If the client also needed the persons' age
, all it has to do is slightly adjust the query and include the new field in the query’s payload:
{
allPersons {
name
age
}
}
One of the major advantages of GraphQL is that it allows for naturally querying nested information. For example, if we wanted to load all the posts
that a Person
has written, we could simply follow the structure of our types to request this information:
{
allPersons {
name
age
posts {
title
}
}
}
Queries with Arguments
In GraphQL, each field can have zero or more arguments if that's specified in the schema. For example, the allPersons
field could have a last
parameter to only return up to a specific number of persons. Here's what a corresponding query would look like:
{
allPersons(last: 2) {
name
}
}
Writing Data with Mutations
Next to requesting information from a server, the majority of applications also need some way of making changes to the data that’s currently stored in the backend. With GraphQL, these changes are made using so-called mutations. There generally are three kinds of mutations:
- creating new data
- updating existing data
- deleting existing data
Mutations follow the same syntactical structure as queries, but they always need to start with the mutation
keyword. Here’s an example for how we might create a new Person
:
mutation {
createPerson(name: "Bob", age: 36) {
name
age
}
}
Notice that similar to the query we wrote before, the mutation also has a root field - in this case it's called createPerson
. We also already learned about the concepts of arguments for fields. In this case, the createPerson
field takes two arguments that specify the new person's name
and age
.
Like with a query, we're also able to specify a payload for a mutation in which we can ask for different properties of the new Person
object. In our case, we’re asking for the name
and the age
- though admittedly that’s not super helpful in our example since we obviously already know them as we pass them into the mutation. However, being able to also query information when sending mutations can be a very powerful tool that allows us to retrieve new information from the server in a single roundtrip!
The server response for the above mutation would look as follows:
"createPerson": {
"name": "Bob",
"age": "36",
}
One pattern we'll often find is that GraphQL types have unique IDs that are generated by the server when new objects are created. Extending our Person
type from before, we could add an id
like this:
type Person {
id: ID!
name: String!
age: Int!
}
Now, when a new Person
is created, we could directly ask for the id
in the payload of the mutation, since that is information that wasn’t available on the client beforehand:
mutation {
createPerson(name: "Alice", age: 36) {
id
}
}
Realtime Updates with Subscriptions
Another important requirement for many applications today is to have a realtime connection to the server in order to get immediately informed about important events. For this use case, GraphQL offers the concept of subscriptions.
When a client subscribes to an event, it will initiate and hold a steady connection to the server. Whenever that particular event then actually happens, the server pushes the corresponding data to the client. Unlike queries and mutations that follow a typical “request-response-cycle”, subscriptions represent a stream of data sent over to the client.
Subscriptions are written using the same syntax as queries and mutations. Here’s an example where we subscribe on events happening on the Person
type:
subscription {
newPerson {
name
age
}
}
After a client sent this subscription to a server, a connection is opened between them. Then, whenever a new mutation is performed that creates a new Person
, the server sends the information about this person over to the client:
{
"newPerson": {
"name": "Jane",
"age": 23
}
}
Key takeaways
Defining a Schema
Now that we have a basic understanding of what queries, mutations, and subscriptions look like, let’s put it all together and learn how we can write a schema that would allow us to execute the examples we’ve seen so far.
The schema is one of the most important concepts when working with a GraphQL API. It specifies the capabilities of the API and defines how clients can request the data. It is often seen as a contract between the server and client.
Generally, a schema is simply a collection of GraphQL types. However, when writing the schema for an API, there are some special root types:
type Query { ... }
type Mutation { ... }
type Subscription { ... }
The Query
, Mutation
, and Subscription
types are the entry points for the requests sent by the client. To enable the allPersons
-query that we saw before, the Query
type would have to be written as follows:
type Query {
allPersons: [Person!]!
}
```i
`allPersons` is called a *root field* of the API. Considering again the example where we added the `last` argument to the `allPersons` field, we'd have to write the `Query` as follows:
```graphql(nocopy)
type Query {
allPersons(last: Int): [Person!]!
}
Similarly, for the createPerson
-mutation, we’ll have to add a root field to the Mutation
type:
type Mutation {
createPerson(name: String!, age: Int!): Person!
}
Notice that this root field takes two arguments as well, the name
and the age
of the new Person
.
Finally, for the subscriptions, we’d have to add the newPerson
root field:
type Subscription {
newPerson: Person!
}
Putting it all together, this is the full schema for all the queries and mutation that we have see so far:
type Query {
allPersons(last: Int): [Person!]!
}
type Mutation {
createPerson(name: String!, age: Int!): Person!
}
type Subscription {
newPerson: Person!
}
type Person {
name: String!
age: Int!
posts: [Post!]!
}
type Post {
title: String!
author: Person!
}
Tips and advice
As you can see, learning GraphQL is rather fun and easy if you've been developing on a range of technologies over the years, as you'll pick up the best of breed methods and implementations permeating throughout the GraphQL ecosystem.
If you're newer to building apps for production use, then you'll love the powerful and easy to use serverless toolchain that comes with building GraphQL powered applications.
Project time
Since it's always essential to practice, let's prepare a sandbox environment to experiment with what we've learned so far.
Final thoughts and next steps
React & Apollo Quickstart project
See the Relay Tutorial instead
For this quickstart, we have prepared a repository that contains the full React code for an Instagram clone. All you need to do is create a Graphcool service that will expose the GraphQL API and connect it with the React application. Let's get started!
Clone the example repository that contains the React application:
git clone https://github.com/graphcool-examples/react-graphql.git
cd react-graphql/quickstart-with-apollo
Feel free to get familiar with the code. The app contains the following React components
:
Post
: Renders a single post itemListPage
: Renders a list of post itemsCreatePage
: Allows to create a new post itemDetailPage
: Renders the details of a post item and allows to update and delete it
Graphcool services are managed with the Graphcool CLI. So before moving on, you first need to install it.
Install the Graphcool CLI:
npm install -g graphcool
Now that the CLI is installed, you can use it to create the file structure for new service with the graphcool init
command.
Create the local file structure for a new Graphcool service inside a directory called server
:
# Create a local service definition in a new directory called `server`
graphcool init server
graphcool init
creates the local service structure inside the specified server
directory:
.
└── server
├── graphcool.yml
├── types.graphql
└── src
├── hello.graphql
└── hello.js
Each of the created files and directories have a dedicated purpose inside your Graphcool service:
graphcool.yml
: Contains your service definition.types.graphql
: Contains the data model and any additional type definitions for your Graphcool service, written in the GraphQL Schema Definition Language (SDL).src
: Contains the source code (and if necessary GraphQL queries) for the functions you've configured for your service. Notice that a new service comes with a default "Hello World"-function (calledhello
ingraphcool.yml
) which you can delete if you don't want to use it.package.json
: Specifies the dependendies for your functions (if needed).
Next you need to configure the data model for your service.
Open ./server/types.graphql
and add the following type definition to it (feel free to delete the existing User
type):
type Post @model {
id: ID! @isUnique # read-only (managed by Graphcool)
createdAt: DateTime! # read-only (managed by Graphcool)
updatedAt: DateTime! # read-only (managed by Graphcool)
description: String!
imageUrl: String!
}
The changes you introduced by adding the Post
type to the data model are purely local so far. So the next step is to actually deploy the service!
Navigate to the server
directory and deploy your service:
cd server
graphcool deploy
When prompted which cluster you want to deploy to, choose any of the Shared Clusters options (shared-eu-west-1
, shared-ap-northeast-1
or shared-us-west-2
).
Note: If you haven't authenticated with the Graphcool CLI before, this command is going to open up a browser window and ask you to login. Your authentication token will be stored in the global
~/.graphcoolrc
.
You service is now deployed and available via the HTTP endpoints that were printed in the output of the command! The Post
type is added to your data model and the corresponding CRUD operations are generated and exposed by the GraphQL API.
Notice that this command also created the local .graphcoolrc
inside the current directory. It's used to manage your deployment targets.
Save the HTTP endpoint for the Simple API
from the output of the graphcool deploy
command, you'll need it later!
Note: If you ever lose the endpoint for your GraphQL API, you can simply get access to it again by using the
graphcool info
command. When using Apollo, you need to use the endpoint for theSimple API
.
You can test the API inside a GraphQL Playground which you can open with the graphcool playground
command. Feel free to try out the following query and mutation.
Fetching all posts:
query {
allPosts {
id
description
imageUrl
}
}
Creating a new post:
mutation {
createPost(
description: "A rare look into the Graphcool office"
imageUrl: "https://media2.giphy.com/media/xGWD6oKGmkp6E/200_s.gif"
) {
id
}
}
The next step is to connect the React application with the GraphQL API from your Graphcool service.
Paste the HTTP endpoint for the Simple API
that you saved after running graphcool deploy
into ./src/index.js
as the uri
argument in the HttpLink
constructor call:
// replace `__SIMPLE_API_ENDPOINT__` with the endpoint from the previous step
const httpLink = new HttpLink({ uri: '__SIMPLE_API_ENDPOINT__' })
That's it. The last thing to do is actually launching the application 🚀
Install dependencies and run the app:
yarn install
yarn start # open http://localhost:3000 in your browser
Congrats! You did it!!!
In the next post, we'll explore more of the big picture with GraphQL architecture with clients, server(less), tooling, and security.
Apollo Tutorial provided by Graph.cool & examples by How To Graphql