Introducing Bifröst – a standalone websocket server written in Crystal

9379e0f68d34f1973b728c1b069135b2?s=106 Published on by Ruby and crystal

TL;DR Bifröst is a standalone websocket server written in Crystal. It’s easy to use and works with any server-side language that can use JSON Web Tokens. You can find the Bifröst server on Github and if you’re using Ruby we have a companion gem to help your server-side code talk to Bifröst.

Why build a websocket server?

When I started building the Void browser extension last year I quickly noticed that adding items from the extension while you have the web app open felt broken. I needed to add realtime sync to improve the UX of adding items from the browser extension.

Void’s API is built in Rails, so the obvious first-pass at websockets is to use ActionCable, however having used ActionCable in several other apps I’ve found it can be memory intensive, often bloating the core app process to over 1GB ram for a single puma worker. Memory usage is one of the biggest pains in deploying and scaling Ruby apps so I'd rather avoid that problem and continue to comfortably fit the Void API on a single 512MB Heroku dyno.

Another option I've used with great success is Pusher, it’s relatively simple, reliable and doesn’t cost much if you’re running a profitable app – again doesn’t quite fit Void’s needs, which is to be very cost-effective to run in the early stages of it’s life.

I was pretty sure at this point the best solution was to write a realtime microservice from scratch and being fairly proficient in Node.js (the metaphorical grandfather of websockets) I decided to pickup a language I haven't used before and write this service in Crystal.

Why Crystal?

After reading Mike Perham’s blog about choosing an alternate language for Sidekiq I wanted an excuse to try Crystal.

I build most apps in Ruby, I’ve helped build many companies on the back of Ruby programs and I still believe it’s one of the best languages and ecosystems out there to build applications with today. Anyway, I’ll save my Ruby ramblings for a future post, suffice to say Ruby is great for APIs and web apps, however handling many concurrent websocket connections might be more efficiently served by Crystal.

How'd it go? 🧐

Flawlessly, I think. Since initial deployment I haven’t had to make any changes or bugfixes, the only changes have been to get it ready for public release and to add a little /info.json API endpoint to see how many clients are currently connected.

The language is nearly identical to Ruby despite being compiled and statically typed which is impressive and also makes it very easy for experienced Ruby developers to grok.

Getting into the nitty-gritty details under the hood Bifröst uses Kemal – a Sinatra inspired micro-framework, it’s super lightweight and should feel very familiar to anyone who’s used Sinatra before. Kemal has built-in support for websockets (as does Crystal) and thus made my job a breeze.

Testing

TDD is an imperative part of my workflow these days and any language inspired by Ruby is going to have a great testing story, Crystal ships with what is pretty much RSpec at the language level so I felt very at home. The one thing I haven’t figured out just yet is how to write tests for the web socket connections – if anyone reading this can help please leave a comment.

Lucky framework

One last thing, I don’t feel I can give an intro to Crystal without mentioning Paul Smith of Thoughtbot who has been writing a web framework in Crystal called Lucky, I haven’t used it yet but it looks awesome and the premise of making nearly everything fail as a compile-time error is fantastic!

Thanks for reading and go check out Bifröst. If you have questions or problems please open an issue on Github.

Related Articles

It seems like the whole world over is getting ready for GDPR to hit and with 10 days to go if you’re still under pressure here’s something that might help.

This is another post coming out of working on one of our products, https://voidapp.co. You often see articles teaching testing or TDD on an example topic, so I thought it was time to start writing about real world testing, with real code.

In a recent project I have been using UUIDs as the primary key type with Rails 5 and PostgreSQL. This can be useful if your objects IDs are publicly exposed and you want to disguise the fact that they are a sequence, or how early on in the sequence they might be ;-)