In the first part of this GraphQL series, we took a quick glance at what GraphQL is and how it looks. We looked at how GraphQL queries return just the data that you need for a specific UI view (contrast that to needing to make multiple calls to piece together data). We also touched on the fact that on the backend you can package data from various services into the response for a GraphQL query. Now let’s take a deeper look at why you might seriously consider using GraphQL schema for your next project, or even an existing one.
Why Does GraphQL Matter?
The main benefit in my mind for GraphQL is this: a way to drastically simplify how you get and send data for client apps. And with that simplification, you make your team more efficient at building products and you place less load on your infrastructure.
Think about how much time your team spends creating infrastructure and code around fetching data into an application or website. These applications make frequent API calls, parse the API responses, cache the results and generally ferry all that data around. Imagine all that code, often hand-rolled for each application, written to handle custom structures of data coming from your services.
With GraphQL, instead of hand-rolling all of that code, we can rely on a client-side GraphQL library and a server-side GraphQL library. These can handle the getting, sending, and caching of data. These libraries eliminate a bunch of code that you would otherwise have to spend quite a bit of time creating. These libraries normalize your data-ferrying code into an understandable and well-defined approach, versus using custom code on every project.
It’s important to note that GraphQL is just a specification, a format for how to query for data and return data. You use client-side and server-side GraphQL libraries that are implemented in order to communicate according to the GraphQL specification. But there are already libraries in the major programming languages and for the major platforms, written by a very active community. This body of tools gives you an advantage.
Another facet of GraphQL worth consideration is the schema. The schema operates as a kind of “contract” between front-end and back-end, and can be used to generate the data-handling functions on both ends automatically. Let’s look at this schema and how it adds the metaphorical grease to reduce friction in the development process.
GraphQL Schema
The schema is a file that contains a list of queries and types of data that can be queried from a GraphQL server. This schema is the “blueprint,” if you will, containing the list of information you can get and the queries you can use to get it.
We use the schema to set up a GraphQL server, and this server receives GraphQL queries and returns the appropriate responses. Imagine the schema is how we reach an agreement between the server-side folks and client-side folks about what data will be available. Moreover, the schema is a “human readable” representation of the data in your system, and that only helps the programmers and maintainers, as industry oracle Martin Fowler acknowledges: “Any fool can write code that a computer can understand. Good programmers write code that humans can understand.”
We can also use the schema itself to automatically set up the server and client functions that move data around. In other words, you can take the schema and automatically generate your server read/write functions. At the same time, you can generate the equivalent functions on the client side for reading/writing to the server.
Looking at the schema
Here is a simplified version of the schema behind the scenes for our above query examples:
type Query {
movie(id: ID!): Movie
}
type Movie {
id: ID! @isUnique
title: String!
budget: Int
popularity: Float!
tagline: String
overview: String
status: String
runtime: Int
release_date: String
vote_average: Float
vote_count: Int
poster_path: String
backdrop_path: String
genres: [Genre] @relation(name: "MovieGenres")
}
type Genre @model {
id: ID! @isUnique
name: String!
movie: Movie! @relation(name: "MovieGenres")
}
Saving time and easing communication
Note the start of the schema contains a “Query” type. This type lists the queries that we can run. In this case there is a single query called “movie” that we can use to request information about movies. We also see that there is a “Movie” type with fields pertaining to a given movie, along with a “Genre” type. When we build a GraphQL query in order to request info, we can use the listed queries to get information. In that request we can select the fields we are interested in from the list of fields belonging to each type. The “@relation” directives show us relationships between types. In this case, we have an array of multiple genres that could be associated with a given move.
This schema file can be a huge time-saver for communicating information between front-end and back-end developers. In fact, towards the beginning of a project we can flesh out the schema, and both the client-side and server-side teams can work from it. On your GraphQL server, you can even return mock data automatically for any fields that don’t pull actual data from actual database sources yet. And then the client-side developer can use the schema and mock data to work away on the user experience. All of this without the server-side folks slowing down the front-end folks. (How many times do front-end developers hear the back-end folks say that an endpoint is not ready yet, or that it has changed drastically?)
Moreover, you may not even need the schema file as a front-end worker bee. You can check the GraphQL server for info on what queries are available and what types of data are present. Querying for server details is known as introspection.
A more complex schema…
Let’s see a more complex schema, against which we can make queries:
type Query {
movie(id: ID!): Movie
tvShow(id: ID!): TVShow
movies(year: Int, page: Int): Movies
}
interface Media {
id: ID! @isUnique
title: String!
}
type Movies {
pages: Int
results: [Movie]
}
type Movie implements Media {
id: ID! @isUnique
title: String!
budget: Int
popularity: Float!
tagline: String
overview: String
runtime: Int
release_date: String
posters: [Poster] @relation(name: "MediaPoster")
trailers: [Trailer] @relation(name: "MediaTrailer")
genres: [Genre] @relation(name: "Genres")
keywords: [Keyword] @relation(name: "Keywords")
}
type TVShow implements Media {
id: ID! @isUnique
title: String!
popularity: Float!
first_air_date: String
last_air_date: String
overview: String
number_of_episodes: Int
number_of_seasons: Int
episode_run_time: Int
seasons: [TVShowSeason] @relation(name: "TVShowSeasons")
posters: [Poster] @relation(name: "MediaPoster")
trailers: [Trailer] @relation(name: "MediaTrailer")
genres: [Genre] @relation(name: "Genres")
}
type TVShowSeason @model {
id: ID! @isUnique
tvShow: TVShow! @relation(name: "TVShowSeasons")
name: String
season_number: Int
air_date: String
overview: String
episode_count: Int
episodes: [TVShowEpisode] @relation(name: "TVShowSeasonEpisodes")
}
type TVShowEpisode @model {
id: ID! @isUnique
name: String!
air_date: String
overview: String
season: TVShowSeason! @relation(name: "TVShowSeasonEpisodes")
}
type Trailer @model {
id: ID! @isUnique
key: String
name: String
site: String
media: Media! @relation(name: "MediaTrailer")
}
type Poster @model {
id: ID! @isUnique
aspectRatio: Float
filePath: String
height: Int
width: Int
media: Media! @relation(name: "MediaPoster")
}
type Genre @model {
id: ID! @isUnique
name: String!
}
type Keyword @model {
id: ID! @isUnique
name: String!
}
Now let’s make a call to see exactly what types exist on the schema. I’ll show some screenshots of queries using a tool called “GraphiQL”, a playground where you can try out queries for data on your server.
In this example, we’ve use a special query called “__schema” to explore the schema of the server. Here we see that there is a “Query” type, along with a “Movie” type and some other basic types. In this case, the “Query” type contains our list of queries. We can also check out the details on any type; let’s see what queries are available in the “Query” type:
Here we’ve used a special query called “__type” to look into the details of a type in our schema. In the server response, we see that we can make a “movie” query which returns data of type Movie, and a “tvShow” query returning a TVShow. There is also a “movies” query for retrieving lists of Movies. We can also introspect on any of these return types to see what fields they contain:
So, we can look through our list of fields in order to see what is available. And we can use these fields in order to build our query for a movie, as we did in the prior article.
Also, from a server development standpoint, with a single option in a GraphQL server, we can turn on mocking. When we do this, the server will return mock data for each field. We can make this data fit the type of data it represents. This could be mock dates for date fields, mock titles where you need a short title, or long mock descriptions where we need longer placeholder text. In a subsequent article, we’ll look more into data mocking.
Takeaways
In this article, we’ve heard that GraphQL can be used to query for the exact pieces of data you need, the schema can help front-end and back-end developers collaborate, and that there are frameworks to auto-generate the necessary code. We’ve seen the GraphiQL playground, for trying out queries. As adoption grows, the community is sure to create and improve tools and frameworks. Will it replace REST-based services? That’s a topic for another article. For now suffice to say that GraphQL is a rapidly growing alternative that should be on your radar.
In the next part of this series, we’ll get into tutorial mode and learn how to get a server up and running. We will also discuss how to set up the GraphiQL playground for trying out queries. Stay tuned.
This post is the second in a GraphQL series we are working on. Look here for the first post.