This is a tangent coming off of a series about Graphene, a Python implementation of the GraphQL spec.
Typically, when I get to resolvers, this is where I see the first objections to GraphQL stand up – if I still have to write all of this logic, why go to the trouble of learning and thinking through a GraphQL interface and not just use a RESTful interface using stuff I already know?
A brief defense of the GraphQL Resolver pattern
Generally, my thoughts on the “Why GraphQL?”-type questions boil down to a simple position: It doesn’t matter what you ultimately end up using – you are going to write code – and you want that code to be good (ie, flexible, reusable, atomized, and well organized) – so why not use a framework that encourages, if not enforces, those principles by design?
I have seen (and sometimes written) plenty of Flask and Express APIs, AWS Lambda handlers, and Python `__main__` operations trying to do far too much logic in the entry methods. You take an input, do all the work you need to do, and return it. Maybe you break out a handful of methods, but then you call those methods in sequence, still from inside the entry method.
In my opinion, a well designed interface should be agnostic about the platform it is running on. The platform should take the input, transform the input into whatever the interface requires, and submit it to the interface. The interface should be consuming the input in a uniform way, no matter where it is running. I should be able to deploy it to Flask just as easily as I could to Lambda, or even as part of a local script.
The GraphQL spec is not going to free you from poorly designed resolvers by itself. But it starts you down the path of single responsibility, open-closed principles, substitutability, interface segregation. Your resolver does one thing – it builds that type. If you need to build it another way, you build a different resolver. And as your interfaces for these types get broken out, separating out your resolver’s logic (the stuff you would have to write for any interface anyway) into appropriate collections of functionality matches the patterns GraphQL makes you take in the rest of your project.
What’s more, if you are working on top of legacy code, there is nothing stopping you from reusing that existing logic – after all, this is just Python. That is, of course, unless you are too tightly integrated into your existing interface, which, in my opinion, should possibly be kicking off a conversation about how the project is organized.
So, no, GraphQL won’t magically save you from yourself (or your teammates), and a long litany of poor design choices.
But – in addition to many other benefits such as schema validation, type checking, a fault tolerate execution order, built-in
websocket support, simplified graph building without the overhead of a graph database, among others – I do think the design of the framework encourages an approach and a way of thinking where the outcome is typically a project that is more durable and maintainable.