"The trouble with having an open mind, of course, is that people will insist on coming along and trying to put things in it."
Terry Pratchet, Diggers
We often get opportunities to use new technology on projects. On one project and with a talented team, we decided to use GraphQL, a query language for APIs originally built by Facebook (now Meta) in 2015 and moved into its own open-source foundation GraphQL Foundation in 2018, hosted by the Linux Foundation.
GraphQL has been in our crosshairs for quite a while as we think it vastly simplifies API queries in our projects, reduce the need for documentation, and improve collaboration between the web and mobile teams. On this article, we share our experiences from our "honeymoon period" of the tech to the "post-honeymoon" period which we hope people will find helpful should they adopt GraphQL whether it be on PHP or other languages into their projects.
This article is not unique on its own, in fact there are plenty of articles, Reddit and Hacker News posts that elaborate their experiences on the technology:
I remember reading these articles before and remember feeling vindicated that I don't feel alone with my grievances on it. We will share ours without having to resort to taking notes from other articles.
Traditionally in the office, we use OpenAPI specifications and Postman collections to communicate and demonstrate usage of our RESTful APIs on our projects.
Based on our experiences with the team when building applications, while we do think that our web and mobile team go along very well, we felt that we were dragged down by negotiating contracts between the web and the mobile team and writing OpenAPI specifications, thinking that there must be an easier way to get our point across. Sometimes changes (or last-minute) to the spec needs to be done and need to be communicated with the team. Often, this requires updating the OpenAPI spec, pushing to Git and informing the stakeholders of the recent changes.
The project we were handed was new to the company, had both a web and mobile app in mind and we anticipated the friction that it could cause between teams. The models and schemas involved in the project are plenty, complicated, potentially mutable and have relationships that could easily go out of control to draft and finalise in an OpenAPI spec.
In our previous project, we had a fixed number of models to query through API endpoints, but to wait for all these queries to resolve also revealed some performance and usability issues to the front which we had to duct-tape a solution to make them much more responsive. Imagine if you have a "To-do List" application and have to make RESTful queries to show the lists and their tasks. The number of queries would multiply based on the number of lists you have, and that's just the naive implementation where you might not even have the opportunity to optimise! This is the "N+1 problem " in the optics of API queries.
After a good look at GraphQL, we knew that it solved some if not all our concerns with going with RESTful APIs.
We've done microservices on our previous project, but on this new project, we decided to go with a monolith. We went with our usual tech stack at the time.
It may not be obvious, but using Laravel for this project is already red flag to some. Unfortunately, we did not catch this until we've gone through the honeymoon period on the project. We'd like to point out that our commentary on the technology is not just limited to the tooling, but also the philosophy and how it fits on our workflows.
When adopting new technologies, I tend to identify periods of time where we enjoy the benefits of the tech and enter a golden age. I tend to call this period the "Honeymoon Period", an analogue to newlywed couples after a period where they exercise maximum tolerance and forgiveness, and the rough patch of their marriage after a certain period where grievances and struggles show up or are magnified, which we call the "post-honeymoon period".
We took our time into reading the documentation and figuring out how to implement GraphQL into the project. We have run into snags into making sure it works, such as making sure we can authenticate requests by savagely and directly modifying Laravel composer packages, checking if we can make Laravel's testing setup suitable with GraphQL calls, and bootstrap the GraphQL composer package into crude ways with Laravel's routing so we can write the mutators, queries and schemas.
In hindsight, we should have already done a fallback to RESTful APIs at the first sign of trouble. But we were persistent just like the honeymoon period of a newlywed couple. There will be some troubles here and there, but we gave it our utmost patience to make it work.
It didn't take more than a week and finally got our first resolver and query to work using stubs of data. However, it came with a price such as:
We pushed through with these limitations which are not the fault of GraphQL. In fact, we found the newfound freedom of enjoying querying what we want or need, the autocomplete functionality when using HTTP clients such as Kong's Insomnia and Postman or more so with GraphiQL which is a GraphQL IDE, and lastly some very strong schema typing and enforcement, reuse schemas and schema stitching. It made demonstrating how to query specific datasets when whiteboarding sections of the project very easy, and we didn't have to worry too much about penning down what was discussed.
The concept of "resolvers" also felt like a very snug fit and analogue to controllers, as in the "C" in "MVC". In a world where controller classes and methods are regularly abused to make API endpoints on the fly with no definitions nor planning, resolvers felt like a breath of fresh air where you are forced to know exactly what data to expect and what to return in them.
We feel that if we continue the project with RESTful APIs and OpenAPI specs, we would have been bogged down with writing down the spec and communicating it. Instead, we continually focused on the business requirements, making sure we have a shared understanding of the project and that we can make changes to the backend and the frontend can easily follow without having to wait to be communicated to or see their product break due to not getting notified ahead about what the full detail of the changes are going to be.
Like most weddings after a period of time, the cracks start to show up on the decision we've made after some time.
While some of these were things we realised in the honeymoon period, a lot of the things we gave up or didn't give notice to red flags started to fester on the project and on the team. For example, file uploads could prove anti-pattern to GraphQL as the language still hasn't figured out how to deal with it. Instead, you need to create a RESTful API endpoint to handle file uploads. Another would be that our Unit/Feature tests started to scale poorly after gutting some Laravel testing functionalities and being unable to utilise them. Not exactly GraphQL's fault, but we also felt that we needed a bit more insight as to how other people test their GraphQL in their backends.
However, the most pressure we've felt from GraphQL was on how verbose it was to implement. If you needed to nest schemas together, you had to be careful to not let it go on infinite loop. The object you return in the resolver and its types need to match your schema. Otherwise, the framework, the band-aid solutions, nor the GraphQL package would not be expressive enough to flag this issue. Perhaps again it isn't an issue to GraphQL, but on the tooling around it itself. But ignoring the tooling, it definitely felt that the cost of penning OpenAPI specs and communicating with the team were just moved into the overhead of carefully writing the schemas, mutators and queries. Writing the schemas felt tedious, but there was an element of certainty to it when you reach the finish line. Whereas with RESTful APIs using OpenAPI, communication can be hard especially to developers where it is not their forte, and the implementation could be a Schrödinger's Cat of regressions, mismatching types or objects.
We also found ourselves to probably want some form of documentation, like the OpenAPI spec. While this feels like a step backwards and anti-thesis to GraphQL (in my opinion), we did not seem to find an appropriate tooling for this. Some exists, but most if not all of them are paid which we did not feel receptive to. The most compromise we've found was document the equivalent of component schemas that we exposed on GraphQL in OpenAPI. However, we decided against this as we felt maintaining another form of documentation without any way of automating it from the verbose schemas we write in the backend is a complete waste of time.
Lastly, the lack of HTTP request methods other than GET and POST feels like a step backwards towards an existing and established spec on HTTP. Try using a browser developer tool to inspect which mutator or query did you use to make a call over a sea of GET/POST requests. It is daunting nor fun to narrow down or troubleshoot specific queries.
After dealing with some schema changes and some refactoring in the resolvers and schemas, I found myself stopping and asking myself a question whether it was worth it getting involved with GraphQL. I couldn't form my own opinion as I wasn't too sure if all these pain points were how GraphQL works. So, I went to Reddit and on Hacker News to check if I am not the only one. What I've read validated my grievances on the tech, and that I finally don't feel alone from the problems I'm dealing in the project.
As much as I love adopting new tech, I hate to say it, but I don't think this was worth it.
The initial concern of having too much planning, communication and writing for the API endpoints were satisfactorily mitigated. The problem? The complexity of doing so was just moved into writing overly long and verbose schemas, queries and mutators. The tooling issues were just the icing on the cake.
On Reddit, Twitter, or on Hacker News, you get sold stories of how much the new hot thing in tech is. Whether it be Kubernetes (which I don't have a post-honeymoon period), Hadoop, Kafka, Blockchain, AI, etc. However, the Survivorship Bias always stands where it states that those that aren't lucky enough to find success in them are always buried by those who hype and promote them with zealousness. I've seen GraphQL promoted in conferences many times here in Perth. But not a lot, or worse nobody talks about the post-honeymoon period of it. So, if somebody harps to you the next best thing, ask them how their honeymoon period is going, and what do they have to say about their post-honeymoon period. If they have nothing bad to say or they haven't been using it that long, always treat the hype with absolute caution.
Will we use GraphQL in the future?
The answer is going to be a resounding "it depends".
We think that this project we worked with really benefitted from GraphQL, given our experience and understanding of it now. But we don't see it as a direct replacement to RESTful APIs, not even a "yet" at the end.
In the future where projects might be poised to be built with GraphQL in mind, we'd also shy away from monoliths and instead do service-oriented architecture (SOA) or microservices. We noticed that on our first rodeo with GraphQL, we found ourselves prone to and committed a lot of spaghetti code which is very typical in monoliths and incurred some tech debt. This is perhaps of our rudimentary understanding of writing resolvers in nests. The defined boundaries by these non-monolithic architectures could help reduce the workload of each [micro] service, excessive cross-referencing, and hopefully have tests run in isolation better. With the help of schema stitching on other implementations of GraphQL, it could turn into a massive powerhouse for development over RESTful APIs.
Another point we'd like to make when adopting GraphQL is that don't do it in a programming language or framework that doesn't have robust support and tooling for it. Even up to this date November 2023, Laravel seems to have no plans to implement GraphQL natively which is disappointing, but quite understandably so. I could only imagine the overhead of maintaining a GraphQL implementation in Laravel could just take away the focus of the community from the framework and its other packages.
One last consideration to keep in mind next time is that we must go back to the roots as to why GraphQL was created. When Facebook built it, it was created in mind on improving content delivery on their users as efficiently and reliably as possible. The fact that you can query many models in one call was an incredible feat back then unless you overload a RESTful API endpoint with too much data. If this is a mission requirement on a project, then GraphQL should be considered as an option into a project.
We've learned a lot on our experience with using GraphQL on our sole project and help pave way into options in the future for new or old projects. Despite all our grievances of the query language, we still use it on that project and will develop on it in the future.
Have you ever implemented GraphQL in your project? Do your experiences match with ours? Tell us what you think, or what you think we could've missed or done better.