1. 16
  1. 12

    I am really excited to see where Axum is going. For context, in Rust there have been two primary web frameworks that I know have been used “widely” in production (lets be real, Rust is still new there). There is Rocket, which focuses heavily on developer ergonomics but made some interesting design choices (specifically on things like using “fairings” rather than middleware, which unfortunately don’t work out in my opinion) and has not reached 1.0 status yet. Then there is Actix-web, which focused on being ridiculously fast. While at this point I think Actix-web is still the best option for production web apps in Rust (given that its the only one with stable versioning, on 4.0 now), it does have its warts and can be a little finicky to get set up right.

    Then there are the new-comers, Axum and Warp. Both are being developed by important people in the community, Warp by the maintainer of Hyper (the main http library in Rust) and Axum by the developers of Tokio (the main async/await runtime in Rust). Warp seems a bit more experimental, using this concept of a “Filter” as the base building block and focusing on composability. From using Warp a while ago, I ran into a number of issues with the Filter concept and it felt a little messy, but from my understanding it is generally liked by people with a lot of Flask experience.

    Axum though, is interesting. Its similar to Actix-web in that it is just a “web framework” and doesn’t have any experimental concepts, but the big things it brings are (1) close integration with the Tower framework (which is somewhat like a web-framework framework of sorts) and (2) fixes a lot of the warts of Actix-web. The close integration with Tower essentially allows Axum to get a large number of middlewares off-the-bat (which is huge) and lets multiple developing frameworks reuse the same middleware logic. The warts it fixes from Actix-web mainly have to do with the traits required for middlewares and whatnot, as well as supporting capturing panics (which Actix-web does not support which can be a problem). From what I can tell, axum borrows a lot of ideas from Actix-web in terms of the ergonomics and improves on them.

    All in all, I think Axum is a really exciting player in the space, and with tight integration with both Tokio and Tower-web, has a good chance at being “the” web framework for Rust once it stabilizes. I’ve only played around with it briefly, and will likely stick with Actix-web for the short term due to the stability, but I’m excited to switch to Axum.

    1. 3

      I think poem also deserves a mention.

      I chose it for its openapi integration and that it generally seemed quite straight forward.

      1. 1

        Ah interesting! I think I have stumbled upon Poem at some point before but didn’t really evaluate it; I believe I was looking at OpenAPI integrations at the time. For anyone interested in a full picture of the web-frameworks available in Rust, they are all listed here (though can’t really vouch for any other than actix-web): https://www.arewewebyet.org/topics/frameworks/

        1. 1

          I’m working on a draft for Poem as I write this. It really does roll out the red carpet for OpenAPI.

        2. 2

          I’m curious what kind of workloads people are using Rust based web frameworks for. I suppose I can see their usage at Google-scale companies, but for the vast majority of other uses, why not just use a language that already has a production ready web framework stack and is even easy to hire for (C#, Java, Python, Node). Sometimes I’m not sure we factor in the human scale factor as well.

          1. 8

            I used to work on a Python (with MyPy) service before, and we decided to build the next one in Rust. The services aren’t equal (the latter needs to be fast, so Python wasn’t going to cut it), but for what it’s worth I feel a lot more productive on the Rust service. Refactoring is fearless, and there is somehow a lot less thinking going on. I just write some code and then look at the errors I get. Easy. Another thing is that the logs are imo much better. We don’t do anything out of the ordinary, but most of the time if the service returned an error, it’s easy to find it in the logs and it is very descriptive and concise. No digging through stack traces to figure out what happened. Just look at the chain of messages.

            Those are just two things that I find has made me more productive in Rust than I ever was in Python.

            1. 5

              I have seen the same thing with my own development, which is that I find myself more productive in Rust than in Python. To add to your point, I also think it is far easier for someone to get ramped up on a Rust codebase than in Python (assuming experience with Rust previously) due to the explicit nature of the language. So biased and anecdotal, I have found that developers are more productive with Rust and its actually easier and faster to scale the projects with more people.

              It would be great to get some data for all of this! But unfortunately I haven’t seen any research on development speed related to language.

            2. 7

              There are two main reasons I’ve seen for companies choosing Rust for web development, one is for very low-latency applications (eg. payments infrastructure) and the other is for high-correctness applications (eg. healthcare infrastructure). I think for the foreseeable future that is where Rust will really shine in the web space, but I think as the other commenter pointed out there is a huge additional benefit to using Rust which is the ease of refactoring.

              From each Rust project I have worked on the tech-debt is far lower than a similar project in Python/Node (I don’t have any experience with C# or Java). Part of my experience is definitely biased, since the Rust projects are usually started by more experienced developers. But there is a huge cost to Python/Node projects that get big, which is the big ball-of-mud paradigm becomes more and more pronounced. Rust, for better or worse, does not compile big ball-of-mud programs because they do not work with the ownership model. Of course, you can use Arcs and Mutexes to get around the borrow checker, but in general Rust forces you to have clean data patterns otherwise it just won’t let you compile.

              This does have downsides, which is that it can be incredibly frustrating for newcomers to understand exactly why Rust is not allowing something. Especially when certain design patterns that work in other languages do not work in Rust (generally, imo, for very good reason). From my experience people with heavy OO backgrounds have a lot of trouble with idiomatic Rust, as it is very different from OO patterns. But the upside is that it almost feels like there is a constant speed of development with Rust. It is slower, sure, but with languages like Python and JS, the development speed is fast at first and slows down as the project gets bigger. Rust is a constant, steady rate. I think that is going to be seen as an incredible win for companies that are investing in Rust now.

              As for hiring talent, from my experience its almost easier to hire for Rust developers due to two reasons. First, if a developer wants to use Rust, they will find you. And second, the pool is smaller but the quality is higher. At startups I worked at using Python or Node, I found it incredibly tricky to interview people for senior positions because people with supposedly “senior” level of experience with Python were all over the place with actual ability. It felt a bit like a fake it till you make it type of vibe. Interviewing people for Rust has felt a lot more safe to me, because if someone knows Rust I am quite confident on a floor for their ability and can actually evaluate them. This is of course anecdotal and biased, but I do think the hiring situation for Rust developers is a lot better than people think.

              In any case, Rust is new in the web space and there are quirks. The ecosystem is certainly not entirely there, though what is available is the best-in-class in my opinion. I think we’ll see early adopters of Rust reap the benefits in a handful of years and I do expect Rust to be considered a “boring” option for web-development in 10 years. Its definitely exciting to see things take off!

              1. 4

                I feel the opposite is true for me when I consider the “human factor”:

                1. Is there really one production-ready web stack for Python, Java, or NodeJS? I can probably name 2-3 for each language, and I don’t really keep up with this stuff. Even infra-framework, major version upgrades and shifting tastes can mean one shop’s Rails app looks rather different from another’s once you get past the most basic MVC structure.

                2. Re: performance, I may not build anything at BigCo scale (any more) but I like having really efficient runtime resource usage even for smaller projects. If my database (PostgreSQL) can scale down to low 10s of MB RAM and idle at 1% CPU, why shouldn’t my web server? I can run a dozen small services on a $5/mo. 1GB VPS or an RPi and it’s great. (The node_modules directories for that many projects would probably fill up my internal storage on either of the above, much less the RAM.)

                3. Along with Go, I appreciate that Rust lets me deploy code without having to hand-match my language runtime version and manage a dependency bundle. IME this tends to get “solved” with Docker or a similar encapsulation model, but now we have yet another set of platform constraints to solve for. (Don’t even get me started on k8s and the accidental complexity is brings to everything it touches.)

                That’s not to say that there aren’t reasonable ways to mitigate all of the above for other more popular frameworks, just that there’s perhaps more variability and “snowflake”-ness to even a boring old Django app than people sometimes acknowledge.

                You should choose the right tools for your business/project/users, obviously, and all of the above may be less important than being able to hire, or draw from a huge ecosystem of middleware and plugins. For me, I believe I can hold off hitting scaling problems — be they performance, complexity, or team — longer by working in Rust.

                Time will tell, of course. :)

                1. 1

                  Actually just your typical django stuff. Except I can do some really nice tradeoffs in terms of global caching, multithreading and async handling, which is nice when you also have to handle some CPU Intense stuffs like calculating deltas or handling JWTs. While also having a far better error handling awareness / story than in python (and better async story than in python). So the whole stuff scales a lot better and is far more stable in my experience.

                  It’s definitely a slower start, which can feel quite cumbersome. But in my experience you get far less bugs that can be prevented by strong typing and no-compromise deserialization requirements.

                  I’m currently writing a nodejs backend with ~5 people while doing FOSS stuff in rust, and the amount of hidden bugs in the nodejs part feels very annoying and backtracking.

                  Oh and I can set the MemoryDenyWriteExecute flag in the systemd unit, which is nice ;)

              2. 3

                I’m currently rewriting a Node API using Axum. Trying to keep it somewhat in the “clean architecture” format. Does anyone have examples of Axum usage besides those that are in the repo itself?

                1. 4

                  There is a RealWorld implementation: realworld-axum-sqlx.

                2. 3

                  I couldn’t find any examples or mention of Zstandard or Brotli in the Axum code base. It could be at this point that Axum would be better placed behind a dedicated reverse proxy if this sort of support is important to you.

                  All the tower middleware are usable with Axum.

                  Like this one https://docs.rs/tower-http/0.1.0/tower_http/compression/index.html

                  let middleware_stack = ServiceBuilder::new()
                  .layer(HandleErrorLayer::new(handle_timeout_er
                  ror))
                          // .load_shed() // ???
                          .concurrency_limit(1024)
                          .timeout(std::time::Duration::from_secs(2))           .trace_for_http()
                          .compression()
                  
                  1. 2

                    That’s a really good point. Cheers for highlighting that.