Site Update: Axum
Published on , 905 words, 4 minutes to read
I have made a bunch of huge changes to my website that hopefully you won't notice unless you read this post that points them out to you. I have redone how the website's URL routing works to use axum instead of warp.
I chose warp fairly arbitrarily when I was getting into the swing of Rust. This
choice turned out to be a bit of a mistake. Don't get me wrong, warp is a
fantastic framework, but overall I've not been happy with how it impacts compile
times. Warp works by pushing a lot of the complexities with HTTP routing into
the type system. This can lead to undebuggable inscruitable types that make it
really hard to understand what is wrong. Here is the autogenerated type for the
/blog/:name
route:
warp::filter::and::And<warp::filter::and::And<impl warp::filter::FilterBase<Extract = (), Error = Infallible> + warp::Filter + std::marker::Copy, Exact<warp::path::internal::Opaque<main::{closure#0}::__StaticPath>>>, warp::filter::and_then::AndThen<warp::filter::and::And<warp::filter::and::And<warp::filter::and::And<warp::filter::and::And<impl warp::filter::FilterBase<Extract = (), Error = Infallible> + warp::Filter + std::marker::Copy, impl warp::filter::FilterBase<Extract = (std::string::String,), Error = Rejection> + warp::Filter + std::marker::Copy>, impl warp::filter::FilterBase<Extract = (), Error = Rejection> + warp::Filter + std::marker::Copy>, impl warp::filter::FilterBase<Extract = (Arc<app::State>,), Error = Infallible> + warp::Filter + Clone>, impl warp::filter::FilterBase<Extract = (), Error = Rejection> + warp::Filter + std::marker::Copy>, fn(std::string::String, Arc<app::State>) -> impl warp::Future<Output = Result<Opaque(DefId(0:1249 ~ xesite[3495]::handlers::blog::post_view::{opaque#0}::{opaque#0}), []), Rejection>> {blog::post_view}>>
What the heck is that? Is that a binary tree?
Yep. It's insufferable to try and debug too.
Yeah, it's really hard to understand what's going on in error messages because of this. This also means that the routes are put into a binary tree in the type system, which means if your tree is unbalanced then you get slower compile times and a slight hit at runtime as the framework traverses your binary tree to figure out what to do. This has also made it difficult for me to add features such as historical views of my RSS feed or other things I want to add like the April Fools day feature I've been working on.
When I went out framework shopping, I tried a few things and got reccomendations from a trusted friend before I finally settled on axum as the heart of this website. Axum has a few major advantages that brought me "in the door":
- It's maintained by the tokio team
- It leverages the type system of Rust to make writing handlers easier
- It uses extractors (think lenses) to help you pick out the subset of data you need, not blindly giving you everything and hoping you figure it out
- It has sub-routers which can have different middleware stacks than the main one (useful for things like API authentication)
And it has these disadvantages:
- Writing middleware is kinda weird (though this may be because I'm not used to working with tower), but easy once you get the general flow of things
- I can't find a way to have the template data get continuously piped to client connections instead of rendering it to a buffer and then writing that buffer to the client
- It doesn't have the biggest mindshare and one of the best ways to get unstuck at the time of writing this article is to ask on their Discord server
Overall, I've been happy with the experience of porting over my site to using Axum. I did a stream on Twitch where I ported it all over if you want to watch the process and hear my thought processes as I was figuring things out.
As users, nothing should have changed about this site. However I'm almost certain that I did forget to port something over, so if I missed something you rely on, get in contact. I have not gotten the Patreon API interoperability code fixed yet, so that is the next major issue. I am going to figure out how refresh tokens work the hard way and make the patrons page auto-updating instead of having to manually get a new API key every month. I am also looking into having that patrons page be updated by a cronjob that emits json to the disk and have my site load from that instead of just hoping that the patreon API credentials are up to date. We'll see how that goes, but you can track that here. I will likely do a livestream for this.
I have also contacted a copyeditor for my blog. I am so happy with the results so far. My Devops post was the first thing that the editor reviewed and they absolutely tore my first draft in half and helped me put the parts back together into something more palateable. I am beyond satisfied with this and will continue to use this editor in the future. I wish I had gotten a copyeditor sooner.
Facts and circumstances may have changed since publication. Please contact me before jumping to conclusions if something seems wrong or unclear.
Tags: