For work, I recently created a proposal to use GraphQL to replace our rather complex Flask REST API, and I figured I'd share the results of that investigation here! If you want, the original proposal can be found here, but this is just a better and more general explanation.
TLDR: Now is not the time to switch to GraphQL if your backend is in Python, and particularly if rely on SQLAlchemy data models.
Our Environment
Currently, the quality of GraphQL tooling is mixed in different languages, so it's very important to consider what your existing stack looks like when considering changing to GraphQL. For us, we had:
- Python for all backend components
- Flask for the REST API, with existing auth middlewares
- SQLAlchemy as the ORM, using declarative models
- React and Typescript for the frontend, with React-Query to help with interactions with the API.
Keep these in mind, as they'll come to be important when comparing options later on.
What is GraphQL Anyway?
It's often described as a query language for your api, but that doesn't mean too much. The basic idea is that instead of numerous endpoints that all return different, well-specified things, you instead have one endpoint and query that for just the desired data. Instead of requesting specific endpoints, you request exactly the data you need according to a strongly typed schema, reducing the need for specific new endpoints for new uses and making adding new frontend capabilities easier later on.
The best way to learn is by example, so to see a GraphQL API in action, you can visit https://graphqlbin.com/v2/7On1SQ, which is my personal API that I use to populate the now playing field on my website. A somewhat more complex example (my friend’s personal API) is here: https://api.horner.tj/, and here is the Mailchimp GraphQL Playground, a much much more complex example: https://mailchimp.com/developer/open-commerce/playground-fullscreen/.
These all are links to the GraphQL Playground, a tool used to test queries and explore GraphQL APIs. In the real world, you'll use one of a few client libraries to access the API, which we'll see in a moment.
Notably, GraphQL is not:
- A replacement for a database - it’s akin to a rest api, not a database
- A drop-in replacement for a pre-existing REST API - while a frontend will likely be able to make requests that return the same data, the requests will look different. Thankfully, fantastic JS/TS tooling already exists for GraphQL and React specifically that will make this transition easier
- A replacement for SQLAlchemy - SQLAlchemy is still used to communicate with the database, again this is pretty much swapped in for the REST API
Switching to GraphQL
TLDR: Switching to GraphQL will incur a time cost upfront, but will likely pay off later.
Pros
- Stronger types + docs for the API - If you're rolling your own API as we were, you likely do not have robust API documentation, or possibly much at all. The GraphQL Schema makes it clear what will be returned from any query, giving you docs for free.
- Faster for the frontend at runtime, requiring only a single request to render everything
- Faster for frontend developers to work with, as there is much less digging through docs (or in our case, the actual backend code) to find the endpoints that return the requested data.
- Enables subscriptions, where a client can subscribe to changes to something and have updates pushed when they occur, which adds possibilities of whole new features
Cons
- Startup Costs: With an existing API, there can be significant startup costs getting setup and transitioned over, which can be hard to justify right now
- Lack of Experience: Almost every developer in the world is experienced with REST APIs to some level, however it's not likely that everyone on your team is already experienced with GraphQL
- Increased Compute for the Backend: unless special precautions are taken, they ways that GraphQL makes requests to a database can be very inefficient at scale
Tooling
Frontend
At it's core, GraphQL is just POST requests with a specially defined body, so we have a number of options for how to interact with a GraphQL server.
Fetch API
It’s entirely possible to not use a fancy library to communicate with a GraphQL server, relying entirely on JavaScript's own Fetch API. Simply make a post request to the GraphQL endpoint with the query as the body and it will return the response as JSON data, simple as that. For example, the query at https://graphqlbin.com/v2/7On1SQ can be represented as the fetch:
fetch("https://api.jakecover.me/graphql", {
body: "{"query":"# Write your query or mutation herenquery {\\n nowPlaying {\\n track {\\n ... on Track {\\n title\\n artists {\\n name\\n }\\n }\\n ... on Episode {\\n title\\n show\\n }\\n }\\n state {\\n shuffle\\n repeat\\n progress\\n }\\n }\\n}"}",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Dnt: "1"
},
method: "POST"
})
Summary: While this is workable, I do not recommend this as it’s really not the best way to use GraphQL, and we would likely be better off looking at something with a bit more
Relay
This takes a much more React-specific approach to implementing a GraphQL client. Instead of each component making its own request to get the data it needs, or having one component request a significant amount of data at the start, Relay has components specify the data they want, then makes a single request to the API and feeds the correct data to said components. This is a somewhat radical departure from the current react query system, but it definitely has its upsides for perf as well as possibly reducing the complexity of piping data around react applications. For what it’s worth, Relay is on V11, so it’s clearly very robust and well designed.
Graphene also has specific integrations for Relay which I have not fully investigated but seem useful.
Summary: Since it uses a significantly different and quite opinionated model of how data should be provided to the application, it will require a large amount of work to implement, however it has the potential to be very powerful.
Apollo Client
Apollo Client is, in my experience, a somewhat more common library than Relay, and Apollo’s other product, Apollo Server, is pretty much the name in GraphQL servers (unfortunately, it is written in Typescript so is not compatible with the rest of our Python data models).
Unlike Relay, this has a very similar interface to React Query and will be much easier to drop in on the frontend. It also adds caching as well as a system for local-only data changes, it can even replace most of the functions of Redux if you ever need that. It also supports GraphQL subscriptions and has a whole slew of other things that can be utilized later on if the need arises.
Summary: There does not seem to be much of a reason to use the fetch API over Apollo Client, as it can behave in a very simple way similar to React Query and later be modified to take advantage of its numerous other benefits. While Relay may be a more powerful choice overall, it will be much harder to implement and I’m not certain the difficulty would be worth it over just using Apollo Client.
Backend
We use Python, so this discussion will largely focus on it's tooling. However, if you're lucky enough to be using JS or TS, you will get fantastic, bullet proof tooling in the form of apollo server that I strongly recommend and certainly is ready for anything you'd want right now.
For our use case, we want to find a GraphQL server library in python that integrates well with SQLAlchemy in order to hopefully reduce or remove the need for separate types / objects between the two, which should help significantly to reduce the complexity.
On the surface level, it seems like the best library for this is Graphene, graphene-python.org, because it seems to be both the most mature python graphql library. It also has a SQLAlchemy integration that allows us to re-use the database models as types for GraphQL and is well documented.
However, while this is all true, Graphene really isn't a good choice, especially if you're planning to use the SQLAlchemy integration. This is mostly because of the state of the Graphene documentation, which is pretty much just abysmal. There is some, but it does not cover things in a useful depth and in many cases isn't really clear enough or go into nearly enough detail. This is even worse if you're planning on using the SQLAlchemy integration, in which there is a quick start guide, some tips, and jack shit else. At this point in time, I'd really avoid these tools.
Well then, are there any actually good options? Not really!!! There are more Python client libraries, but none have an SQLAlchemy integration at the moment, and most aren't quite ready yet. However, there is one library, Strawberry, which does show a lot of promise, even if it isn't quite ready for prime time yet. If I were you, I'd keep your eyes on that space and see where that goes, I believe it will make python graphql into something usable in a year or two.
Summary
So to wrap up:
- GraphQL is cool
- It has fantastic tooling if you're willing to stick with JavaScript / TypeScript all the way down
- It does not really have good Python tooling yet, although that is changing