Ultrafast, minimalistic GraphQL
⢠⢠âď¸ 5 minute readGraphQL is the running horse many webapp-developers are betting on. It is an API gateway that glues multiple services together, it connects frontend with backend and it makes querying data from multiple datapoints convenient. It also forms a bottleneck if not carefully implemented.
Surely such an issue can be patched by spawning a load-balancer in front of it and problem solved, but there are better solutions which offer additional benefits. A first one is switching to serverless, enabling autoscaling without the need of server configuration. Second is implementing it in Go.
Why Go? You might ask. Well, I delved into many programming languages looking to build a minimal template which supports: type-safety, scalability, security, an ORM and which makes use of all cores with co-routines. Next to these obvious features I also wanted a minimal end result, which could be achieved by opting for a compilable language. One of my favorite next-gen languages is Rust, which originally is a system language, but became notorious for its performance in other fields as well (such as Data Science and REST development). In Rust, we have Juniper, Actix-Web and Diesel which form the holy trinity. An example implementation shows us the downside however. Rust does everything right in terms of the contraints I was imposing, but the community is still small. In future articles I will explain how it could be used in production systems, f.e. with the intent of patching mission-critical bottlenecks. But building a full production backend would first take a couple of months of additional practice. A reference shows us that the Rust Ecosystem is on the rise still, and I would highly recommend it when your only objective is to build a REST API or a microservice. Another way of benefiting from its performance is by the use of Message Queues such as ZMQ.
After having created some affinity with Rust I delved into the more well known solution for modern webapps: Go. Golang (or Go in short) is a statically typed, compiled programming language designed at Google, it is syntactically similar to C, but with memory safety, garbage collection, structural typing, and communicating sequential processes-style concurrency. I have read the following book and I highly recommend it to anyone interested in building a next-gen webapp-backend:
It does not cover the topic of GraphQL however (that is partly the reason why I am writing this), but it covers all topics that you should master prior to building your first backend. When reading I became fascinated by the go-routines and cross-compilability of the language. IMO the language itself was more easy to get started with, however Rustâs package manager Cargo outclasses Goâs Dep (for now).
Serving HTTP(s) requests
A first challenge is choosing a good web-server which handles the bulk of our requests. The framework should be very fast, crash-free and needs to support middleware & HTTP/2. I often find myself going through some web-frameworks benchmarks in order to guide my options. A quick look at Go shows us fasthttp would be the way to go, but they do not yet support HTTP/2. An option I am also looking at is taking the even faster Rust web-servers and bind it into my Go code. But for now, I decided to start with the second placer Gin, as I could refactor it later on:
Gin delivers on all fronts and is in my experience a lot better than the default net/http package. It is also crazy fast in comparison with Express (the Node.js communityâs favorite nowadays).
Flexible ORM
The next thing to consider was how to manage our database models in a type-safe manner. Disclaimer: In cases where models are simple I would highly suggest the use of querybuilders instead, as they offer a more minimal and quicker approach to getting your data without the loss of querying power. However when DBâs get more complex, ORM handle table management and migrations better.
I opted for the choice of the Go community:
GraphQL Toolkit
The GraphQL toolkit I was looking for needed to support a schema-first approach. Next it should offer the basics such as interfaces, enums and dataloader. Finally it should be easy to work with. Looking at the following comparison I realised such a framework does exist in Go. GqlGen automatically parses your schema into manageable Go code, you just need to finish your resolvers and your ready to roll. The automatic request routing, under the hood, is also implemented with gin-gonic.
The end result:
Beat that with node_modules
As there are already many blogs that cover the last three frameworks I would rather focus on how I managed to build a complete GraphQL server with an image size of 4MB. Whereas on average with Node.js and Python we get the same functionality and a Docker image of +200MB.
The first distinction is the compilability of the language. The latter two languages are interpreted. An interpreted language is any programming language that isnât already in âmachine codeâ prior to runtime. Unlike compiled languages, an interpreted languageâs translation doesnât happen beforehand. Translation occurs at the same time as the program is being executed.
I am still working on a complete multi-staged build, but for now the binaries are compiled to linux-executable binaries in the compile.sh step. Similar to Rust, Go can compile to linux-executable and bare metal binaries being able to run on any device. Next I have build a first stage which copies these binaries, strips and compresses it with UPX. Yeah you read it right, you can even further compress your machine code. In the final stage, the executable is copied to a scratch image, resulting in an ultra compact GraphQL server (hence its name).
The repository serves as a template, with only one mutation implemented (as a functional example of the ORM). I will perform some benchmarks next month. In the meantime, feel free to report any issues and make sure to take a look.