A few months ago, I briefly talked about using gqlgen to create a backend for a now “postponed” project. And after that, I have been trying to create a GraphQL wrapper for the official HackerNews REST API.
And about a week ago, I finally said “screw it”, delete everything, and restart from scratch with GraphQL.js
So what went wrong? Is it because of Go? Is it because of gqlgen? Or is there something/someone else to blame? Let’s find out!
So, the premise of the project is simple: the REST API is kinda weird, since it returns (almost) exclusively just ID. Clients will have to fire a billion extra requests to get enough data to show on screen. This is probably not a good thing, especially for mobile devices, so let’s use GraphQL as a proxy, and expose the flexibility to the client. The (presumably) powerful server does most of the heavy lifting, and in might even be able to add some clever caching mechanism at some point.
So I started the project with Go and gqlgen, mostly because of 3 reasons:
- I want to practice my Go skills
- I have use gqlgen before
- The premise of “type safe + codegen” seems like a perfect fit for GraphQL
And I still stand by all those reason why I chose it in the first place. And at the beginning, it doesn’t take long to whip out a super simple “happy path only + not efficient” schema + resolver + query. But the moment I start to drill down a bit, it starts to get kinda convoluted. Here’s a list of things that I have to figure out how to solve with my very limited knowledge of Go & gqlgen:
- If the query only ask for id, it should not invoke any extra API call. That means I need to tell gqlgen to use resolver to get every-single-field-in-basically-every-single-type. It gets really verbose real fast.
- The ID for item (story, comment, job listing, poll, poll option) is an integer, while the ID for user is a string (like handler for twitter), but I can’t teach gqlgen to interpret GraphQL’s ID type to 2 different type at the same time.
- It took me way too long to realized that I should at least put the resolver functions into another file, so that when I have to update the schema and regenerate resolver.go, I don’t have to painfully put the logic back in -_-
- Update from dep to mod. Not really a problem, just something I have to deal with.
And after like 2 month, I really starting to feel like I am fighting both Go and gqlgen at every level. I just want to get a Hacker News API in GraphQL done. There’s got to be a better way.
Since I don’t control Hacker News, nor its API, I will have to adjust how I tackle the problem. Here’s a list of things that I have re-consider during that time:
- Type-safety: Maybe for this project it matter less? The output definitely needs to match what the GraphQL schema specified, but the internals don’t have to be. In fact, there are some benefit to not having type-safety, so that there are less ceremonial type cast/check/conversion.
- Framework: Having schema-first with codegen + a billion lines to configure it is probably not what this project needs. Just do a typical resolver-first approach.
- Developer ergonomics: I am still very much in Java-esq-languages-land, and trying to figure out pointers, basically no OOP, and not knowing how to do basic parallelism, means I probably spend more time going the wrong way, run into dead end, and go back and realize what the right approach is. Don’t get me wrong, it’s nice to learn, but it’s not nice to feel like you have accomplish nothing.
So after considering all of the above, I decided to give GraphQL.js a shot. And fast-forward to today, I have already build a lot more of the resolvers comparing to the old one with Go + gqlgen, so I think it’s safe to call it a success (for now).
So what’s the takeaway here?
- Use the right tool for the right job.
- If you have good reasons, don’t be afraid to do some experiment, and even restart what you are working on. It might actually pay off.
- 10x engineers are the future of society, and you have to learn how to spot and keep them. /s