by Michael Hunger

How we built the 2018 World Cup GraphQL API

After the second round of matches at World Cup 2018 got underway, we wanted to create an easy way for people to answer all their questions about the teams involved.



We’ve created a Neo4j backed GraphQL API for World Cup 2018. You can play with it here.

Building a GraphQL API backed by Neo4j

We’d already created a database with all the World Cup Data for people to use and query with, but we wanted to make the data accessible to people who don’t know Neo4j’s query language, Cypher.

GraphQL to the rescue!

Before we get to that, let’s first take a look at the Neo4j graph model that we’ve created.


The WorldCup node sits in the middle of our graph, and all other parts of the model revolve around that. We have one WorldCup node for each tournament.

Next up we have the host Country which is connected to the WorldCup node by a HOSTED_BY relationship. Matches also belong to a WorldCup, and each Country names a Squad of Players that will represent them in a WorldCup tournament.

A player is connected to an Appearance node for each match that they participate in either as a starter or substitute. If they score a Goal, the Appearance node will connect to that Goal node.

The GRANDstack Starter Kit

Ok that’s enough Neo4j, let’s get back to GraphQL.

The GRANDstack combines GraphQL, React, Apollo and Neo4j Database into an easy to use bundle for quickly building APIs and apps. It uses the GraphQL schema to automatically transpile GraphQL queries to single Neo4j queries and is able to auto-generate all queries, mutations, and fields from the annotated schema.

GRANDstack Logo

We used the starter kit to create a GraphQL API on top of our existing Neo4j database.

It consist of two parts: a backend api and a front-end ui. The backend serves the GraphQL API and also a GraphQL Playground (a really neat browser and editor for GraphQL queries), which also provides the data schema, docs, and auto-completion.

We forked it to our own repository and then merged it back to a branch worldcup for you to use.

The first step is to create a GraphQL schema. You can see what we came up with below, which closely matches what we have above in our Graph Model.

A minimal schema looks like this:

We extended it quite a bit with some GRANDstack specific Neo4j extensions to have some alternative mappings and so on.

grand-stack-starter - Simple starter project for GRANDstack full stack

Once we’d defined the schema, we updated our .env file to point to our Neo4j Cloud ( hosted database.


You can run this locally by executing yarn && yarn start. That will launch the Playground at http://localhost:4000, where you can start to play around with some queries.

We can write some queries to find the world’s best player.

GraphQL Playground

Of course we can find out much more about him.

Result for details on Messi

Deploy to

Now we’re ready to deploy. We could deploy our service to anywhere that hosts Node.js apps, but @Will.Lyon recommended Zeit Now — a great and easy to use service for hosting your app that has an easy to use free plan for small projects.

We just install the service and then run the now command in our directory to deploy. For stable URLs, you can alias the project to a fixed name.

The GraphQL server is deployed at and is ready for use now. Let’s have a look at the types of queries we can run against the dataset.

Portugal vs. Morocco

As I’m writing this post, Portugal is playing Morocco. We can check for the latest score by executing this GraphQL query in the playground defined above.

Portugal vs. Morocco results

Portugal are 1–0 up at the moment, and it’s no surprise to learn that Cristiano Ronaldo was the scorer.

Who is Cristiano?

If we want to learn more about Cristiano, we can query for players as well. For example, the following query will show us his date of birth and how many goals he’s ever scored in the World Cup, as well as how many goals he has scored this time around:

So he’s got 4 goals in World Cup 2018 and 7 in total, which means he’s got more goals in this tournament than the previous ones combined!

Germany’s score in 1990

Although Germany didn’t get off to a great start in this World Cup, we can write a nostalgic query to find out the score of the 1990 World Cup final:

Bad times in 1966 :(

Or we could look back to 1966, as my colleague Mark forced me to do:

Keeping the data fresh

The database is being updated via a Lambda job every few minutes while matches are being played, so the data should be reasonably fresh whenever you query it.


The React UI

The front-end ui is basically just a React app that uses Apollo Client to query our API and render the results in components.

Please note that the current React code is really ugly and horrible. We leave that as challenge to you to build beautiful web and/or mobile apps using the World Cup GraphQL API. :)

my (ugly) world cup screen

Of course you can also use Vue or Angular or other ui-frameworks you love.

It connects to the URL provided in the .env file, where we just put either our local http://localhost:4000 or our URI.


Again, a single now command deploys our UI as well. In our case we don’t need it, but Zeit now has support if you have any secret credentials.


GRANDstack Hackathon

Luckily, the GRANDstack Hackathon is still ongoing to gather great ideas and there are some really cool prizes for your submissions.

Thanks a lot to my colleague Mark Needham for doing all the hard work of putting the data and model together.