A exploration of using GraphQL to query a Rails 5 API

In a previous post, I described some of the minor frustration our team experienced using REST. This post is more tactical. What I’d like to do here is to set up the absolute minimum you need to see GraphQL in action on a throw-away Rails app.

There are a ton of examples of starting your first serious app with Rails and GraphQL — I watched an excellent YouTube video from a fellow who spent the first 15 min setting up the Rails app and then got around to adding in GraphQL. We don’t need another one of those; his was fine (watch it here).

So I’ll skip the bells and whistles and let’s get right to some code.

Our simple app is an API we can query: We have a cute little town called Catoville, and our domain model consists of:

  • some office Buildings; each has a name, and a year it was built.
  • some coffee houses, too, so people working in the Buildings can go get a caffeine fix; each of which also has a name and a telephone number
  • we magically “know” the walk time & distance of a Building to a Coffee House. For simplicity we’ll model it as a ∞:∞ relationship named Caffeinations, rather than getting all GIS on ourselves with longitude/latitude and great circles and the like.
  • and it’s a public API, so we won’t worry about an auth token.

That’s it, otherwise it wouldn’t be a simple example. I’ll assume you don’t need to see a step-by-step of setting up a standard Rails 5 api app and we can concentrate on GraphQL

We’ll start with Models along the lines of the following:

I added in some data by hand based on the image above, since there were so few records. You might do it via a seed file or etc.

Our API will respond to the three following requests:

  1. Return a list of Buildings
  2. Given a Building id, return some details about it
  3. Given a Building id, return details about the Building’s associated Coffee Houses

Let’s get started:

Add the GraphQL gem to your Gemfile:

You can add  gem graphiql-rails since it lets you play around with the api, especially in a dev environment. But you probably already have something like POSTMAN in your browser, or similar, which is all we really need.

The graphql gem can set up some standard directories and basic code for you. Not required, but very useful. let’s do that.

Modify your application.rb paths:

At the time of this writing, I had to manually add the paths above. Hopefully this will get fixed in the install process soon.

Add a route for the API endpoint:

We want to respond to POST /graphql?query={[your_graphql_query_here]} so our routes.rb  file looks like (yeah! the install process already set this up for us):

Add a GraphQL controller:

We got a free totally standard GraphQL controller set up by the install process, too! It’s standard because the schema we’re about to define actually does a lot of the controller’s work.

You can see near the end of the  execute controller method, that we’ll call the execute method on our schema. Wait! We got a standardized one of those too for free from install

As you can read on the GraphQL website, basically we’ll need to define some object types  we’re willing to serve up (in our example, for Buildings and CoffeeHouses), and what data on them we are willing to make available for consumption via API. Then we connect them to a schema that makes them available for querying. Then we execute queries against that schema.

Add a schema:

As mentioned earlier, we won’t bother we any mutations, so ignore that for now. What we need here is to define what our QueryType should do.

Define the query types:

Looks very controller+route-ish, right? “if you send me a request for ‘buildings’, I’ll return a array of BuildingTypes, and it will include whatever the BuildingType specifies it wants to do when it has a collection from the Building model via Building.all ”

And, “if you send me a request for ‘building’, I’ll need an ‘id’ argument, and I’ll use it to return whatever the BuildingType specifies it wants to do when it has a single Building model instance”

All strong typed and introspect-able.

Define what we want BuildingType to return:

So whenever a BuildingType is needed the above defines what data will be allowed out. Note two things:

  1. things are strongly typed, so you really know what you’re getting back. There’s an entire library of standard types you’d expect (String, Int, etc) as well user-defined types which is what we’re specifying right here
  2. a consumer of the API will only get back the subset of these BuildingType fields that it specifies in its query. So although BuildingType can tell you about  id ,  name , year_built and  coffee_houses , it’s only going to send you what you ask for.

This highlights one of the rationales for GraphQL: that the backend devs would specify all the things that the BuildingType can return, and then the front end devs (or consumers of the API) will specify what they need. Time saved in not having to go bug the backend people to write a new or modified end point, and no need to send large chunks of data which are available yet not needed by the requestor.

Define what we want CoffeeHouseType to return:

We’ll make a Coffee House data type too, because the Building type says it has the ability to return an array of coffee houses, so when it returns one what makes up each of those Coffee House objects?

Note I commented out the telephone  field, so even though it’s part of the model, CoffeeHouseType will no way of passing that to any request. In a “real” app, I’d’ve skipped including it all together.

Ready to call the API:

That’s pretty much it. Start the server, and let’s call the API! Remember, I’m using the POSTMAN browser plugin, but you can do this any way you like.

Of the 3 target endpoints we want to make available, the API call #1 (“give me all buildings”) looks like:

I find it easier to just focus on the GraphQL itself, since the path is constant, so I’ll refer to the above as:

where  buildings comes from our query_types.rb file wherein we described what we’d respond to, and name is the field of BuildingType that we want back, nothing else (at this time)

We get back a JSON response of

Precisely what we asked for, no more (we always get “data” as the top-level portion of the successful response).

Oh, you wanted the year the building was built? Well, just ask for it

asking to give me back  name  and  year_built but call it simply  year

How about API call #2? ‘Give me a particular building, with an id I specify’, here I want building with id=3, and the  name  and  year_built :

yielding:

And, finally, API call #3, info about building 5 and coffee houses associated with it. Oh, and let’s call this a  office_building rather than a building :

which lets us easily return associated records, in a single call. Note, too, I do need to tell the CoffeeHouseType which of its fields I want back:

Whew! That’s it! Anything more and this would be an outright tutorial. But it does give a quick overview of how easily we can use GraphQL as part of an API.

Things I wish GraphQL would do (maybe it does, and I haven’t found it yet):

  • When I want ALL fields of a data type, I wish that I could get them without having to enumerate. Or, if I specify none, then perhaps it ought to mean all? This was a very simple example, but I can imagine a much more robust data type with a dozen fields. Or maybe this functionality exists, and I just missed it.
  • And, I feel a slight bother that we’ve delegated so much of the routes and controller functionality into the GraphQL schema. Yes, I can have many files in the app/graphql/types directory to break things up. But I feel some sort of abstraction might be possible to put a lot of the schema functionality back into the controller. Or maybe this is just better thought of Model-Presenter ?

Definitely I encourage you to seek out all the many additional GraphQL tutorials out there in the wild, now that you know how to put the pieces together with your Rails-based API. And GraphQL can happily co-exist with your “regular” REST API, so you can slowly expermient on existing projects!

 

GraphQL on Rails, part 2/2

Post navigation


One thought on “GraphQL on Rails, part 2/2

Leave a Reply

Your email address will not be published. Required fields are marked *