GraphQL on Ethereum

How GraphQL middleware can help you fetch blockchain data more efficiently

HTTP-based APIs allow us to fetch data, invoke actions, and send updates to remote systems. One of the more popular HTTP API architectures is REST, which uses the HTTP verbs GET, POST, PATCH, and DELETE along with a path like /car/405 to interact with the state of remote resources.

REST and the N+1 Problem

Let’s pretend that you’re a developer at RiNo Rentals, a car rental agency. You’ve been placed in charge of building the site’s inventory page. The page should show thumbnails of the vehicles available and some basic information; users can then click on a thumbnail to view more information about the vehicle and reserve it.

You start by looking at the documentation for the RiNo Rentals REST API in order to find the route that will give you a list of all of the available cars. In order to fetch data you’ll use the GET verb with the /cars route. This REST endpoint returns a set of car IDs by default as JSON.

From here you can iterate over the car IDs listed in the cars property and fetch them using the GET /car/{id} route. This returns detailed information about the car.

The problem, however, is that most of the data returned by the GET /car/{id} route isn’t relevant to the inventory page. All our users want to view is a thumbnail of the vehicle along with the make and model for easy browsing. We can just ignore unneeded information, but the issue becomes that we have to write logic that extracts data from this response. If the API team decides that the thumbnails data needs to move to a new route called GET /car/{id}/thumbnails we have to comb through our app and make the necessary changes and make the requests for the thumbnails as well—meaning that we need to either delay rendering the car listings until all the requests finish or have a loading state.

Which brings up another issue. Our web application users had to wait for the GET /cars call to finish and return data, then parse the response, then create an additional request for each individual car. This is known as the N+1 problem for REST APIs that follow the index+detail model. For every N items that you wish to fetch details for, you’ll have to make N+1 requests to the API. One for the index and one for each detail.

This behavior continues throughout the RiNo Rentals application. As the platform begins to support reviews, user notifications, and per-site inventory, the shape of the API responses change and new routes are added like /car/{id}/reviews. Soon, the application is making dozens of requests for a single view.

GraphQL is an elegant solution to this problem. A client is able to send a smaller number of requests to fetch the same amount of data and API services that understand GraphQL only return the data that the consumer asked for—nothing more.

This is done by sending an HTTP POST request to a single endpoint on the API containing a query in the GraphQL syntax. This allows you to take the multiple requests from before and combine them into a single request. By creating a GraphQL query that requests cars that match a specific query and specifies only the attributes that you need returned for each child element, you can reduce the complexity of your application’s codebase and minimize time mapping and transforming API responses.

Since RiNo Rentals has added a new GraphQL endpoint to their backend API, we’re able to build a single query to fetch only the information we need to draw our inventory page, and get only the information we want back in a single response. We construct a GraphQL query (first below) that fetches only available cars based on a specified filter and specify which attributes we want returned for each car. This means that we get a single JSON response (second below) that gives us the information we need to construct our car tiles and nothing more.

The backend engineering team has added a new attribute that we’d like to use in our inventory page: reviews. This attribute is actually implemented on the backend as a One-to-Many relationship since the team has created a new `review` object and built the relationship to car objects into the database. Previously there would have been discussions around whether or not to include review data in the /car/{id} response or to make it a new /car/{id}/reviews endpoint, but with GraphQL the backend team can simply make the attribute available on the car model and API consumers can decide whether or not they want the data at query time.

We’d like to show some of those reviews when a user clicks on a car tile in the inventory screen so we simply add review information to our query.

The new GraphQL endpoint’s ability to abstract away the complexity of multiple routes, traversing index+detail endpoints, and data resolution has made developing the car index much easier and made the RiNo Rentals experience much faster for customers.

The Jump to Ethereum

Now, RiNo Rentals has decided to start making their rentals available on the public Ethereum blockchain and have appointed you to lead the project after your success with the highly efficient and fast web app.

To allow cars to be reserved and paid for via the blockchain, RiNo Rentals has created a specially designed smart contract that is deployed on the Ethereum blockchain.

This smart contract emits Reserved events whenever a customer reserves a car by invoking the reserve function.

Once the contract is deployed on the Ethereum network, it’s available for use and can be interacted with any Ethereum client node or node service. Instead of maintaining and operating your own nodes, you opt to sign up for DEPLOY, create a new Ethereum node, and are able to interact with the blockchain immediately. Now you can quickly get back to working on the RiNo Rentals blockchain-powered booking process.

As you start querying your DEPLOY managed Ethereum node you start to notice a similar behavior to that of the old RiNo Rentals API. Based on some web3 example code you’ve started using HTTP requests to call JSON-RPC methods. Each request only contains one invocation of a function on the Ethereum node. These functions have names like getBlock, getTransaction, and createFilter and begin to feel a lot like the index+detail model you experienced before.

This sounds like a perfect job for GraphQL!

GraphQL On Ethereum

DEPLOY Shared nodes with the Geth client have GraphQL middleware preinstalled, and Owned nodes can add it with the click of a button.

With the built-in Geth GraphQL endpoint, you can query blockchain data using GraphQL and get all the data you need and nothing more. This endpoint does not perform any artificial indexing or analysis of the chain data, but simply allows you to fetch more data from the blockchain at once.

Geth’s GraphQL implementation shines when querying large collections of transactions or smart contract events. Since these events have nested information and relationships to other attributes, GraphQL’s attribute resolution allows developers to pull entire nested collections of transaction data in a single query.

The RiNo Rentals team would like to be able to see all of the addresses that have reserved vehicles in a range of blocks in order to reward repeat customers with a discount on vehicles they reserve frequently. Previously this would be done by creating a filter on the Ethereum node to look for transactions that emitted a specific smart contract event, receiving a collection of transaction hashes that matched the filter, and fetching detailed information on each transaction via a separate JSON-RPC request.

Instead, with GraphQL we can fetch all the logs for our deployed smart contract, filter for our reservation smart contract event, and get transaction details from those events in a single query.

With a single GraphQL query you can fetch filter data, save bandwidth, and save processing time by only receiving the information you really need. In this case we are only interested in the event data itself and the address of the account that made the reservation.

We’ve received our list of bookings and can now map those down and calculate our most frequent customers.

Ethereum GraphQL’s Limitations

Geth’s GraphQL implementation is currently limited to the following queries:

  • block and blocks— Used to fetch an Ethereum blocks by number, number range, or by hash. If neither is supplied, the most recent known block is returned.

  • pending — Used to fetch information about pending transactions that haven’t been mined yet but are in the mempool

  • transaction — Used to fetch information about specific transactions. This information is limited to raw transaction data; it does not offer any decoding of smart contract events that happen as a result of the transaction or decode raw transaction to indicate what intended action is being performed on smart contracts.

  • logs — Used to fetch log events emitted by smart contracts within a block range for a specific target.

  • gasPrice — Used to fetch the current gas price estimate.

  • protocolVersion — Used to fetch the network protocol version that the Ethereum node operates with.

  • syncing — Used to fetch the state of the Ethereum node.

In addition to the above queries, Geth’s GraphQL implementation only supports the sendRawTransaction mutation which is used to submit a signed Ethereum transaction to the network.

Try It Now

At RADAR we have created a Node.js starter application to get your hands dirty with GraphQL and Ethereum using real blockchain data. Our test application steps back from the mystical world of RiNo Rentals and examines Basic Attention Token and Tether ERC20 token transfers. To use it, you’ll need nodes on the Ethereum network using the Geth client, and GraphQL middleware installed.