DEV Community

loading...

State of Rust Web Frameworks (Server, DB)

readredready profile image Charles Gibson ・2 min read

I've seen two frameworks consistently the most talked about: Rocket and Actix-web.

Rocket and Actix-web seem to have a positive user experience and are more complete frameworks, but Rocket (for now) uses Nightly and does not have async yet and Actix-web's maintainer recently changed.
Warp and Tide are also drumming up excitement.
Hyper—and soon async-h1—perform at a lower level and form the foundation of Warp and Tide respectively.
(More at: lib.rs/server)

Tokio, Actix and async-std bring async functionality to the frameworks. Tokio is tried and true, Actix is great but is only used for Actix-web, and async-std is the final evolution of Tokio.
(More at: lib.rs/async)

For databases interfaces, diesel ORM is the most complete and popular solution. SQLx is angling to be the most perfect and Rusty solution (async, pure Rust, most DBMS, compile time checked).
(More at: lib.rs/db)

With stability, performance, features, and learning curve in mind which is best?
Are any worth adopting yet? Why (not)?
Are they only usable for niche projects? What use cases?

Thank you for your major contribution robertorojasr!

Takeaways

Server

Actix-web is the best complete framework today. It has LTS, a community, excellent performance, and a well-equipped vertical integration. However Rocket(v0.5), Warp, and Tide should quickly challenge it.

DB Interface

If you like using ORM solutions, diesel is ready to go. Otherwise, depending on your DB and bravery you might want to wait for/jump into SQLx or try one of the many DBMS-specific option.

Overall

There are viable comprehensive Rust solutions ready-to-go out there. You can build a speed, lightweight, simple server as an at least moderately experienced Rustacean and BE dev today with limited but meaningful granularity. For less experienced devs (like myself), Actix is the most suitable full solution, Hyper for very lightweight simple solutions, but otherwise you may run into problems with lack of documentation, shifting support, and lack of features and abstractions towards simplicity.

I will try to keep this up to date as the discussion continues. Please notify me if anything is misrepresentative or inaccurate, or to contribute to the post.

Discussion

pic
Editor guide
Collapse
ghost profile image
Ghost

I don't know about "best" but I can with absolute certainty say that the ecosystem is getting increasingly fun, I've done some things with Actix-web, is great, now I want to go lower, and I see 2 routes: Tokio -> Hyper -> Warp and async-std -> async-h1 -> tide. The first is more "mature", with quotes just because even tho Hyper has been used a lot, even by other frameworks Warp is very new; on the other hand the Tide path is even newer but making big strides.

A quick simple summary: in Rust, the async executer is not part of the std, because of technical reasons beyond this summary, not long ago the only player was Tokio, but then async-std came along, it has "std" in the name but is not more "official" than Tokio, is just the name. On top of the executers Tokio and async-std (they have other goodies not just the executer), there are libraries to do some basic stuff, like de/serializing http requests and responses, configuring the server, etc. in there we have Hyper and now async-h1 (which is maybe a month old and is not yet integrated in Tide). On top of that there comes the frameworks, that have some extra goodies, like routing and some middleware like loging, cookies, etc.

You can, without much trouble work directly with Hyper (and I assume the same will happen with async-h1), you would have to do your own routing with some regex and the middlewares you need or add other libraries. As I see it Actix-web and Rocket are like Django, Tide and Hyper are more like Flask, Hyper and async-h1 are the libraries not longer frameworks; of course the line is blurry and with Rust you always see more of the belly of the beast.

On the DB side Diesel is almost the de-facto ORM, is great, you have everything checked at compile time, so you are sure your queries are fine and datatypes are checked at compile time which is great, and you have also the non-ORM route with libraries like rust-postgres or tokio-postgres (same thing but async), and the new crate (how Rust calls its packages) SQLx, that lets you write plain SQL queries but checked both integrity and data types at compilet ime which is beyond awesome, it actually check with the real database every query at compile time if you want.

The ecosystem is very new in some places but is getting very good, so simple to understand, so low level yet so clear I'm far from expert but I could make a simple app with Hyper in a couple of hours, its worth at least to check the API docs to have an idea, the documentation is great and after years with Django now I understand a lot of things that where black magic to me.

I hope this is somehow helpful :)

Collapse
ghost profile image
Ghost

I leave you a couple of posts of one of the top devs in the async-std side of the force:

Here explain why Tide, and what they are triying to achieve with it
blog.yoshuawuyts.com/tide/

Here he explain the changes they are making right now, Tide still uses some Hyper, but now they are getting rid of that dependency, and making more modular the whole thing.
blog.yoshuawuyts.com/async-http/

Collapse
ben profile image
Ben Halpern

Very helpful!

Collapse
readredready profile image
Charles Gibson Author

Thanks for your contribution! I posted some questions you might be able to help me with.

dev.to/readredready/comment/ncm1

Collapse
cryptoquick profile image
Distributed Hunter Trujillo

Great overview, and very concise. I've appreciated Warp's ease of use so far. Never needed an ORM, but I have used Sled with Serde. It's also worthwhile to mention a view layer. Percy and Warp are a great combo for SSR + HTML content, in addition to a more React-friendly syntax. I also appreciate Iced's approach, since it leaves HTML + CSS behind, in favor of a more graphics-native approach, providing draw call abstractions to Vulkan, Metal, and WebGPU APIs. This allows it to support desktop applications like Electron, in addition to a lightning fast web interface, without all the bloat.

Collapse
readredready profile image
Charles Gibson Author

I'm very unfamiliar with the graphics bit, so I appreciate you bringing up Iced. I didn't know people used other methods of representation in web apps. Is it more performative? It would surprise me to hear that it is more ideal for most web apps, but for gaming apps or graphics/animation heavy apps I see the merit.

Would you use it over other solutions? Why?

PS Iced and Percy are more "another post" material, so I will leave anything not related to servers or DBs off the main discussion thread. But definitely thanks for bringing more tools to the fore. cheers

Collapse
cryptoquick profile image
Distributed Hunter Trujillo

For sure! Iced is quite novel in that you can use the same codebase to cross-compile entire complete apps across multiple targets. So far web and WASM (with low-level WebGPU, as opposed to WebGL) binaries and desktop targets are officially supported. It uses native Metal (on MacOS) and Vulkan (on Linux and Windows) graphics APIs directly. Additionally, an iOS example does exist, where Rust is compiled to arm64-ios.

The advantage of this is many-fold. The first obvious one is that you don't have to have, for example, different codebases for responsive web, Electron, and mobile. It's just the one, and Rust has a similar feature to the C preprocessor (but not the same) that lets you disable the inclusion of certain code paths in the final product, while still keeping the code in the same file and context. Builds can be so much more optimized-- A static linker can do fine-grained dead code removal optimizations, and removal of debug code in production, in ways that JavaScript could never hope to do. Dependency tree-shaking doesn't even come close. You can't do AST tree-shaking JS reliably because you can redefine methods at runtime, as well as a number of other nonsense things that are possible in JS that, although are edge-cases, would preclude more traditional optimizations that happen all the time in traditional software development since, well, the late 1970s. And since most modern JS projects still have an incredible amount of code, even in the final production binary, it's easy to see why the edge cases matter. Even with TypeScript, the big problem with dead code removal in JS, and there are some experimental projects that attempt this, careful analysis and testing of the entire bundle, including the dependencies, would be necessary to ensure safety of that kind.

And since Rust doesn't require the inclusion of a traditional GC, it's a perfect fit for WASM. WebAssembly doesn't support a GC OOTB, it's still in the proposal phase. Go and Blazor still have to include a GC.

Ironically, Rust dev build artifacts (the target directory) wind up taking up a lot more space on disk than node_modules and dist or build. I'm assuming rustc is just doing a heck of a lot more when it's doing the mostly comprehensive ADT type-checking Rust is famous for. But the web release binary is usually comparable to a React app, if not samller. Additionally, the native build is usually around 10-100x smaller than an Electron app. If you're feeling real adventurous, you can even compile without support for CPUs made 10 years ago, or even try recompiling the Rust stdlib in a similar fashion, but that's also a lot harder to do, admittedly.

There's a lot more to say on this topic, and it goes real deep, but needless to say, I think Rust and WASM has a real shot at a future where it could replace Electron, React, and React-Native. In addition to, you know, the promise projects like Lucet and WASI have towards entirely outmoding Docker and Kubernetes for deployments. And projects like Sled and TiKV have real promise for high-performance KV stores. More scalable (and cheaper) than Redis, while faster than LevelDB, which, to a degree, helps lessen the need for fiddly fine-grained tuning like what is commonly done in RocksDB. Remember, pretty much all databases are essentially key-value stores because of their indexing strategies. They just have different query frontends and other implementation details here and there. But they usually all have WAL or AOIL or whatever, and there'll be background optimization processes, and oh hey, now you have something resembling a B-Tree or LSM tree.

But, as you mentioned, all that is a discussion to be had in a different post entirely. One I'd love to have, though, and perhaps we could compile it into a separate dev.to post.

Collapse
readredready profile image
Charles Gibson Author

Thanks for adding to this. I didn't get into DB implementations in Rust because I thought it might get into the weeds a bit (I didn't even plan to do DB interfacing either).

It sounds like you don't always need a DB for your projects. If that is so, what attracted you to a Rust solution (Sled with Serde) rather than a standard DB solution (PG, MySQL, etc)? Can you speak to the pros and cons of it, and would you recommend Sled or another DB impl.?

I would be scared to do my DB in something without tons of documentation and support. I would think a lot of people would have concerns about LTS and portability.

Collapse
cryptoquick profile image
Distributed Hunter Trujillo

I mentioned a few of these points in the other comment, and as for LTS and long-term viability, I'm really speaking a lot more to the future than anything. I'm quite practical when it comes to work I do for my employers, but a large part of the value I usually bring to anyone I might be working for is my breadth and depth of knowledge, especially of things that might be years and years away. I like to have a big toolchest, so I can pick the right tool for the job, or push back if something proposed that sounds like a poor fit. It's a lot easier to do research spikes when you're always doing mini-spikes of your own!

I missed this post originally, but feel free to keep the database discussion here, if you like. :)

Collapse
ghost profile image
Ghost

I'm not an expert but my take on those:

With stability, performance, features, and learning curve in mind which is best?

I've worked a bit with actix-web for a while, nothing big, but so far seems very stable to me; I think that in this moment in the ecosystem the lacking "stability" is more about that because a lot is new, is subject to changes, so you may have to updat your code with each version, for more long term projects now I would recomend actix-web or plain Hyper; Rocket has been around for a while but still uses nightly and soon should adopt async so I think the codebases may have to change soon. So far Rust in general has never done anything unexpected and doesn't make easy to cut corners. As you may see, a lot of packages advertize now #![forbid(unsafe_code)] which is a big plus; feature wise is not as complete as say Django, but is getting there, and in general I see more of a "Flask" aproach, not sure any framework is interested in become a "Django", everyone seems focused in making everything as modular as possible.

About the learning curve is tough, it took me a long time to grasp Django (I know I mention Django a lot, but I've worked with it the most), is so big and does so much that is difficult to understand the whole picture, a lot to do your first little project; with, for example Hyper, you build bottom up, so you get the whole picture easier, also is lower level so you instantly understand the whole thing and how simple it is and how every piece fits together. You check docs.rs/hyper/0.13.3/hyper/server/... and even not knowing Rust you can get what is doing it and you build from there.

Are any worth adopting yet? Why (not)?

Absolutely yes, I don't think actix-web will get another big change, the API is already pretty stable, in Rust the version numbering is a bit more "organized" than in other PL, so hit the 1.0 means something and Actix is already close to v3, Tide devs have said it, they have the line "not ready for production" in their page not because the code may fail but because they may do big changes with each version, so you have to adapt your code accordingly so is more of an "operation instability" not that the code is not well baked; Hyper is also being used by a lot of other projects (is a library not a framework) so they are careful to not break things. I've learned a lot this lasts months with Rust, things that I avoided and even feared when using Django are clear to me now.

Are they only usable for niche projects? What use cases?

I don't have enough experience to tackle this one but I don't see why not. Of course there is the natural difficulties of a smaller user base, is harder to hire Rust devs than PHP, JS or Python. You have less people around that can help you, etc. But is a matter of time, I'm sure that Rust is gonna get bigger and better, fast, we haven't even stretch its legs with webassembly yet, most wasm frameworks are using old models, but in time I think you'll have all the tools to make your whole stack in Rust, from the DB to the browser.

Collapse
readredready profile image
Charles Gibson Author

Wow, thanks for your insight!

Besides Rocket and Tide having LTS, it seems like there is plenty of good options. If you forced me to start today I would probably just get into Actix-web to be on the safe side. But since I have my hands full at the moment, so I get to wait to see how Tide develops. The async-std seems like an attempt to put out a Rust standard standard and its developed by the people who made Tokio, so it should be mature soon and stable even in the mean time. It sounds tidy too!

I also will get to see how SQLx matures since I prefer writing in SQL.

I think anyone who reads your input will be pretty tuned into what's going on with Rust web frameworks today.

Collapse
mremanuel profile image
Emanuel Lindström

Thanks for sharing.
I also have experience with Django and agree with it being kind of a monolith. It does a lot and is hard to grasp at first. But when you learn it, it's super nice to work with.
I just finished my first hello world in rust and I'm thinking of diving in the deep end and building a web app. Can you recommend a good approach? I've looked at Rocket and actix-web and I kinda find myself looking for something similar to django. Especially the django ORM is super nice.
Although, I wouldn't mind going a bit lower in abstraction now that I'm getting in to rust. To "do it right" so to speak. My plan is to build a simple, full stack web app. A portfolio perhaps where I can showcase projects, build a rust game, etc. Maybe play around with logins and user authentication.
Can you point me in the right direction in what kind of stack I could use? I don't mind if it's a single framework or one for frontend and one for backend. Thanks!

Collapse
ghost profile image
Ghost

I would recommend an approach similar than what I would have liked to do with Python, to take a look at Flask before Django, if you get directly into Django, after a while, with Django, I felt more like a "configurer" than a programmer, is like filling the little blanks Django left for you.

I would start taking a look at Hyper, maybe do a hello world with it, after that depends on your goal, is to explore or to build something you have already in mind. For the first I find really interesting what is being done with async-std>http-types>async-h1>tide and just understand whar role play each of those parts have gave me, in a week, more understanding of the whole thing than years of Django; if you already have a project in mind and is kinda complex i.e. you want good middleware support, cookie based auth already done, TSL, websockets, etc. I'll go for Actix-web. It doesn't come with ORM like Django but is much more feature rich than other (not sure about Rocket, I've never used and barely look at it). The database stuff is decoupled entirely from the rest so the choice of web stack doesn't affect your db availability.

I bet you'll get a lot just at looking the hyper server example. Notice that Hyper is not a framework is a library, but you'll realize than is not so different, and even if you go for Actix-web, a look at Hyper will make the whole actix-web structure, clearer.

That is what I've gather as a noob in the whole thing. And if you came from Django, Askama will feel like home with templating, in fact is based in the syntax of Jinja, so with some minor adjustments, you could even reuse your Django templates, and as is completely decoupled with the rest (DB and http server: actix-web, tide, hyper, etc.) you can mix and match.

Collapse
ghost profile image
Ghost

Thanks for the shoutout! glad that it helped, your post ended up very concise and clear, congrats. If I may add, the whole async-std/async-h1/tide has a very active discord chatroom, (links their respective lib.rs pages), and as is so new, they are very open to questions and suggestions and someone more knowledgeable than me could make a huge impact now that everything is new in the whole chain, from the base executor to the framework. And by the way take a look at Askama too, it's the templating crate I'm using now, is pretty much Jinja for Rust, templates are compiled and type checked, the syntax is pretty much the same, great alternative for SSR.

Collapse
stevepryde profile image
Steve Pryde

I wouldn't say that "async-std is the final evolution of Tokio". They are both in some sense competitors and in another sense merely alternatives with slightly different philosophies, strengths and weaknesses. There is passion on both sides which is (I think) overall positive for the community. Personally I've used tokio a little more but I'm keen to try out async-std a bit too once its ecosystem matures a little. So far I haven't found a http client as capable as reqwest (which is built on hyper/tokio) on the async-std side but hopefully that will change soon. Currently looking at surf but it still has quite a way to go in terms of ergonomics and usability I think.

For servers I've used rocket including for a real project at work and it has been running solid for a good 6 months now. I highly recommend watching the youtube talks by its author to learn about all of the ways rocket is designed to prevent you from making common mistakes, while reducing boilerplate as well. I've also used diesel a lot too, and it is fantastic. The ability to have your queries checked at compile time is amazing and saves so much time. In addition to these I've played with websockets both with the websockets and tungstenite crates, and that has been pretty awesome.

One of my more recent projects is a small online multiplayer game - originally written in javascript, then ported to typescript, and recently I converted the server entirely to Rust using async/await. It talks to a typescript client using raw websockets (no socket.io layer) and that actually works pretty well. There are some interesting performance challenges to deal with when interfacing between javascript async/await and Rust's async/await in a game loop running at 30 fps on both server and client. Actually the client runs at 60fps but I only send inputs every second frame. So far it works very well.

Collapse
0xbf profile image
Bo

I am also keeping an eye on tide, I think it is very promising

Collapse
deciduously profile image
Ben Lovy

It's a fast moving and active ecosystem, but I am personally most excited about tide.

Collapse
ghost profile image
Ghost

and probably in the next version they will adopt the new dependencies and ditch the remaining bits of Hyper, I think it will get a lot cleaner, and that's saying a lot. Also as far as I've seen plain async-h1 will be as usable as Hyper ;)

Collapse
stevensonmt profile image
stevensonmt

I've tried my hand at several of the frameworks mentioned and find Warp to have the easiest learning curve for getting up and running. For doing more complex things actix is probably best because of the more extensive documentation out there from end users (blogs and tutorials) with Rocket a close second. If you have a background in web frameworks from other languages, you'll probably find some projects trying to duplicate those experiences, like Roa mimicking Koa.js.

Collapse
readredready profile image
Charles Gibson Author

Why is Hyper listed on the HTTP client (not server) side? What is it capable of that separates it from the rest? Is it losing its place in the Rust web framework ecosystem?

Is there anything about the underlying components Tokio, Actix, Hyper, or async-h1 that makes the solutions built on top of them better or worse than the others? What should people be looking out for?

DB interfacing seems like an underdeveloped part of the ecosystem. Should people wait to see how this area develops before migrating from standards like Node, PHP, etc.?

Collapse
ghost profile image
Ghost

Why is Hyper listed on the HTTP client (not server) side?

No idea, Hyper has tools for both as you can see in the API docs, maybe is something silly like lib.rs keeps in only 1 category and (C)lient goes before (S)erver. In fact I made a little app with the server side.

Is it losing its place in the Rust web framework ecosystem?

I don't think so, in fact Warp is builded on top of Hyper, the later is not a framework, is a library, so many go directly to Warp (in fact both are made by the same people, together with Surf the client counterpart of Warp)

Is there anything about the underlying components Tokio, Actix, Hyper, or async-h1
that makes the solutions built on top of them better or worse than the others? What
should people be looking out for?

I don't think so, more of a stylistic choice AFAIK, if you are already familiar with Tokio you'll probably prefer Hyper/Warp, if you already know other framework that uses Hyper you may want to just get lower in the stack; Tokio and Hyper are also older and more established; async-std is a clean slate, it was build by former Tokio devs, some keep working on it, I like those revamps, but is a personal opinion; also projects like Tide are working in abstract their deps so you may use Tide with the Tokio stack in the future.

DB interfacing seems like an underdeveloped part of the ecosystem. Should people
wait to see how this area develops before migrating from standards like Node, PHP, etc.?

I'm not sure, I haven't felt constrained by Diesel compared to Django ORM, and other non-ORM tools are taking another more simple approach, so don't need as much code, in SQLx you write actually SQL, no new syntax, no dsl. And I would argue about "standards" of PHP or Node because they are different beasts, Rust can do what others can't, to check at compiletime the integrity of the queries and also tie the type of the native code with the DB types is a big deal.

One of the big reasons to use a ORM in the first place is to be certain than the queries are well written, here we can have that and more in plain SQL, if SQL can do something, then the intercafe can too, is a more direct and simple approach possible in part because of Rust.

About performance I haven't looked much, but I would surprised if is somehow slow in comparison.

And of course there's always some practical considerations, you are migrating you existing PHP codebase, why? are you doing it on your own or will need extra people?, how much time/effort are you willing to take, etc.

Collapse
ben profile image
Ben Halpern

I'm also curious about this