Tutorial: Create a Sports API using GraphQL

In a previous post, I discussed the merits of GraphQL and the value it provides to API consumers / developers. I wanted to reinforce the discussion with a hands-on example of creating a GraphQL API from existing REST services. This tutorial assumes you have familiarity with GraphQL so we won’t go over basic concepts.

I have been an avid sports fan, both following professional leagues as a fan and as a weekend warrior playing recreationally. As a fun exercise, I wanted to create a Sports API to track my favourite teams and players from different professional leagues.

Sports API

  • GET /api/team/list?league={league}
  • GET /api/player/list?league={league}

where league is either [nba, nhl, mlb, nfl]

Favourites API

I want to create a new API where I can search for either my favourite team or player. Using the above REST APIs will require me to write code that invokes each API, searching for the team, and then combining all the individual responses into a single message. As you can imagine, this code will be quite complex for the API consumer (or frontend developer) who simply wants to retrieve the data for their Web or mobile app. An alternative approach is to write this API using GraphQL.

The full source code for this project can be cloned/downloaded from here: https://github.com/ozairs/graphql.

Run the Sports API by opening a command prompt and navigating to the root directory. Enter the command node index.js.

The request /response flow is the following:

POST /sports/graphql? HTTP/1.1
query {
Teams (name: "Toronto", league: [nhl, nba, mlb]) {
name
city
arena
}
}
HTTP /1.1 200 OK
{
"data": {
"Teams": [
{
"name": "Toronto Maple Leafs",
"city": "Toronto, Ontario",
"arena": "Air Canada Center"
},
{
"name": "Toronto Raptors",
"city": "Toronto, Ontario",
"arena": "Air Canada Center"
},
{
"name": "Toronto Blue Jays",
"city": "Toronto, Ontario",
"arena": "Rogers Centre"
}
]
}
}

Now that is much easier! I have flexibility to choose the attributes returned name city arena.

Let’s explore this project in more detail. It contains 4 key files:

  • index.js (express server running graphql)
  • player.js (searches for players across all leagues)
  • team.js (searches for teams across all leagues)
  • sports.graphql (schema for the request and response)

The sports.graphql file provides the schema for the API consumer. The key part of the schema is the query field, which provides two operations (Teams and Players) that can be performed.

type Query {
   Teams(name: String, league: [League]): [Team]
   Players(name: String, league: [League]): [Player]
}

The response for each query is Teamand Player which are defined in the schema.

type Team {
   name: String
   arena: String
   city: String
   homepage: String
}
type Player {
   name: String
   league: String
   position: String
   team: [Team]
   picture: String
}

In the index.js file, the resolvers variable defines the code that is executed when the query is performed:

The players.getPlayers and teams.getTeams functions from the player.js and team.js files use the axios module to asynchronously call each of the 4 leagues to retrieve the requested players or teams.

The above snippets just shows you how the first response is build, other responses are built in a similar manner.

The key take away is that GraphQL does not remove the complexity of invoking multiple REST services; instead it provides a simpler framework for the API consumer to retrieve API data.

The other key benefit of GraphQL is the ability for developers to navigate the graph of the returned data. For example, once I return the list of players, I would like to obtain information about their team without making another call. For example:

POST /sports/graphql? HTTP/1.1
query {

Players (name: "John T") {
name
league
team {
name
arena
city
}

}
}
HTTP /1.1 200 OK
{
"data": {
"Players": [
{
"name": "John Tavares",
"league": "nhl",
"team": [
{
"name": "Toronto Maple Leafs",
"arena": "Air Canada Center",
"city": "Toronto, Ontario"
}
]

}
]
}
}

In summary, GraphQL provides a simplified framework for developers to return server side data without strictly adhering to API interfaces. It moves the complexity of retrieving data from the API consumer to the server runtime, where additional resources can be deployed to provide enhanced performance; and abstracting complexity from frontend developers who can focus on building the user interface.