GraphQL server up and running with 50 lines of python

Aug 26, 2017 · 5 min read

Prefix: I actually wrote this before choosing to use the Elixir project instead, but decided to release this post anyway because it was actually the fastest to set up.

Node.js is far from the only server implementation out there for graphQL. I recently looked a C# and python implementations to see how quickly I could attach a graphQL server to our database here at hyr and was amazed at the great options I found.

I love well-documented projects, and found the .NET project a bit lacking in terms of setup instructions and good examples. The python project, on the other hand, consisted of a full documentation site and an decent example to get you started. Furthermore, there’s something beautiful about the simple exchange of resources between python files.

I’m going to show how to get the server going on top of an existing SQL database, which is an awesome. I’ll be using Graphene, which is a graphQL library that works with my things like Django. But for simplicity I’ll be using SQLAlchemy as the ORM. It’s very similar to Django. For the web server I’ll be using the trusty Flask.

Moving from REST: reflect the database

If you already have an existing database, perhaps with a RESTful Node.js API, it can be a pain to construct new models in a different language. That’s where database reflection comes in. It’s going to do this work for you.

If you’re using postgres, you should know about the postgraphQL (Node.js) library. It similarly reflects (collects information from) your database and constructs not only the ORM(models), but also the graphQL web interface(schema). It’s a completely automated add-on resulting in highly opinionated structure for both queries and mutations. In this example, using graphene and SQLAlchemy, you’ll find almost the same ease of setup, but you’ll have infinitely more room for customization!

The file structure for the graphene server app (modeled after this example) looks like this:

graphene_example/
|-app.py
|-database.py
|-models.py
|-schema.py

Two of these files are simply configurations, database.py and app.py which you can think of as establishing the back and front-facing connections respectively. Let see how that works.

From db to client

The database.py simply creates a connection to the database and a session object which will be used for queries. The session object is then attached to the Base class that will be inherited each of the models in models.py.

database.py

Two things here. Depending on your setup the connection string may vary a bit. But for the most part, it will look something like this

'mysql://scott:tiger@localhost/foo'

or for postgres:

'postgresql://scott:tiger@localhost/mydatabase’

Second thing is DeferredRefection. What this does is it allows field definitions from your database to be inferred for each class after all of the model classes are declared. You don’t have to list out each of the fields for a given db table. Let’s say we already have a database with two existing tables. We connect the model to the table with __tablename__. Then we only have to define primary and foreign keys and their relationships.

models.py

Note I did not define a name or age field for the PersonModel. The db reflection takes care of this unless your db field doesn’t map to a SQLAlchemy type. For example, if you are using an MSSQL money type you will need to add a Column for it with an appropriate type.

rate = Column(Float)

That’s it! The majority of the work here is defining relationships.

Creating a GraphQL endpoint

On the other side of things is the actual graphQL part. Let’s start by covering the actual flask web server.

app.py

Most of this is fairly boilerplate. You create a single route that is handled by a graphQL view function. That’s all graphQL is — a single endpoint! Not much else to say here. Let’s look at the schema.py

schema.py

For most python developers this is perhaps the only new thing. Anyone not familiar with graphQL concepts, I highly recommend reading the graphQL docs and watching Greg Hurrell’s deep dive video.

The first half of schema.py merely imports the models and creates new classes that are usable by graphene. It establishes the inheritance from the SQLAlchemyObjectType and a node interface which is used internally by graphene (and Relay if you choose to use it) to establish a global id reference for any instances of that type.

At it’s most simple implementation (read-only graphQL) the schema object needs one thing: a Query. Your Query defines the entry point, or the root (or roots) of all data retrieved by your app. Think of this as the perspective or the viewer of your app’s data (a common Relay pattern actually uses “Viewer” as the root). In this case the Person is the viewer so I’ve defined the person as the root query name.

All root queries besides node must have an accompanying resolve function which allows access to the parameters given to the query.

An aside here about Relay (and Apollo). Adding the node root field is an requirement for Relay. It enables the Relay client store to query the graphQL endpoint and ask for a node by a single globally unique Relay id . Your calls will use the person root while behind the scene, Relay will use calls to node to refetch data only when it needs to — like to update a single data point in a node. It’s a priceless optimization.

Let’s look at an example query.

query{
person(uuid:1){
name
age
}
}

Notice I provide a parameter to the root query of person. That parameter uuid must be defined in the Query and implemented in resolve_person to return the person object using a call to the database or in our case, an SQLAlchemy query.

query{
person(uuid:1){
name
age
Articles(first:5){
edges{
node{
publishedOn
text
}
}
}
}
}

Here is a query that includes a relationship or, in graphQL terms, a connection. The edges field will return an array of nodes, each of which can be referenced in a client app like so:

res.data.person.Articles.edges.map(a => <Text>{a.node.text}</Text>)

This somewhat naively displays the text of the first five articles associated with person whose uuid is 1 in React Native given the response data provided by this call as res. It is my convention to use capitalized words for connections. Other examples will use ArticleConnection.

Mutations

The most difficult and controversial part to any client-side graphQL implementation is the mutation configuration — the write. It’s why the Relay community is anxiously awaiting the release of Relay2. On the server side, things aren’t that bad. I’ll leave it as an exercise for the reader to implement a CREATE mutation. Here is slightly more complex UPDATE:

And it is called like so:

mutation{
updatePersonName(uuid:1, name:"Bob"){
person{
name
}
}
}

You’ll notice that the mutation has a query written into it. That’s the response. Every mutation is a PUT followed by a GET. That should be enough to get you setup with a fairly simple graphQL backend. Please excuse my bad Python.

One last note. SQL calls are not batched so all calls are n+1. Leave a comment please if you know of a good batching solution.

Cameron Moss

Written by

all things js, react-native + redux, foster parenting, social justice

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade