Show HN: Rust Web Framework

(github.com)

201 points | by levkk 3 hours ago ago

65 comments

  • throwaway313373 2 hours ago

    I would kinda expect REST framework to be able to generate Swagger (aka OpenAPI) definitions out of the box. That's one of the killer features of FastAPI in my opinion.

    Also, I don't really understand what is the reason for creating your own ORM instead of integrating with, let's say diesel.rs [0] and what is the reason for inventing your own template language instead of just picking one of the most popular existing template engines [1].

    Other than that this project looks really interesting and I will definitely keep an eye on it.

    [0] https://diesel.rs/

    [1] https://crates.io/categories/template-engine

    • levkk 2 hours ago

      I tried Diesel years ago, it was too "Rusty" for me. It made you define your schema twice, and couldn't use the same structs for inserts and selects (so 3 times, really). Overall, great first attempt at matching database types to Rust types, but the ORM needs to be more flexible and ergonomic - it's supposed to make writing queries easier, not harder :)

      As for templates, writing your own language is almost a right of passage into 30s+ nerd club. I never read the dragon book, but I always wanted to take that class in school. There will always be different implementations of the same thing, and writing this one that mimics very closely what ERB (Rails) does felt right.

      • xyst 6 minutes ago

        Never been a fan of ORMs in general. Working with the extra abstraction layer on top of SQL can be painful.

      • the__alchemist 2 hours ago

        Same: I was put off by keeping track of models in triplicate, and the lack of automatic migrations. These are considered features, vice bugs; it's not for me.

        • phibz 7 minutes ago

          I tried diesel about 5 years ago. I needed to do relationships where the foreign key lived on the other side of the relationship from what diesel wanted. IIRC diesel only supported the key on a specific side, I think it was a M:1 relationship. Diesel docs said this was unsupported. I was still learning traits at the time but navigating how to implement this in diesel was beyond me. I used sqlx and moved on.

    • bryanlarsen 27 minutes ago

      https://github.com/poem-web/poem is one Rust framework with swagger definitions out of the box.

    • fmbb an hour ago

      Know of any similar frameworks that work the other way around? Where you can Keep an openapi definition as the source of truth and validate that your server follows it, I mean.

      • florianmartens a minute ago

        I agree. API-first is the way! Change your schema, auto-generate types into your code and use them for your server definition. It's just faster and more secure this way. Use api-fiddle.com or spotlight.io to work with the schemas (branching, sync with Github).

        In a fully typesafe world, it should be pretty hard to derive from the shema this way.

      • giancarlostoro 34 minutes ago

        As others suggested, I would diff against a generated one, then potentially treat the generated one as source of truth in the future... Then diff accordingly as it changes.

      • internetter an hour ago

        You can simply diff it. Define the OpenAPI spec, have the framework generate a spec, compare.

      • jjnoakes an hour ago

        You could generate a spec for the service and then diff to the expected perhaps.

    • steveklabnik 17 minutes ago

      100% agree that generating OpenAI from your server is a killer feature, it works well for us with Dropshot.

    • outside1234 an hour ago

      Just FYI - since this is an asynchronous framework you probably would want something like sqlx versus Diesel (which is sync if I recall correctly)

  • kvirani 3 hours ago

    Nice, congratulations. It must feel so surreal launching this!

    One of my biggest learnings from doing a bunch of web MVC through Rails over the years is that the framework should heavily discourage business logic in the model layer.

    Some suggestions:

    - Don't allow "callbacks" (what AR calls them) ie hooks like afterCreate in the data model. I know you don't have these yet in your ORM, but in case those are on the roadmap, my opinion is that they should not be.

    - That only really works though if you not strongly encourage a service aka business logic layer. Most of my Rails app tend to have all of these as command aka service objects using a gem (library/package) like Interactor.*

    * It's my view that MVC (and therefore Rails otb) is not ideal by itself to write a production-ready app, because of the missing service layer.

    Also, curious why existing ORMs or query builders from the community weren't leveraged?

    Disclaimer: I haven't written a line of Rust yet (more curious as the days go by). I'm more curious than ever now, thanks to you!

    • ecshafer 2 hours ago

      > One of my biggest learnings from doing a bunch of web MVC through Rails over the years is that the framework should heavily discourage business logic in the model layer.

      I am curious where this comes from, because my thinking is the absolutely opposite. As much business logic as possible should belong in the model. Services should almost all be specific more complex pieces of code that are triggered from the model. Skinny controller, Fat Model, is the logic of code organization that I find makes code the easiest to debug, organize, and discover. Heavy service use end up with a lot of spaghetti code in my experience.

      The other part is that from a pure OOP pov, the model is the base object of what defines the entity. Your "User" should know everything about itself, and should communicate with other entities via messages.

      > Don't allow "callbacks" (what AR calls them) ie hooks like afterCreate in the data model. I know you don't have these yet in your ORM, but in case those are on the roadmap, my opinion is that they should not be.

      This I agree with. Callbacks cause a lot of weird side effects that makes code really hard to debug.

      • yoyonamite a few seconds ago

        It's because people ended up with models that were thousands of lines and difficult to reason about. Out of curiosity, did you end up running into this issue and how did you deal with it?

      • idle_zealot an hour ago

        This sounds to me like the standard OOP versus Data Oriented programming divide. You want to think of code as a bunch of bundles of data and associated functionality, GP wants to think of code as data models and services or functions that act on them.

    • sodapopcan 2 hours ago

      > * It's my view that MVC (and therefore Rails otb) is not ideal by itself to write a production-ready app, because of the missing service layer.

      This is quite the claim. I despise service objects, personally. They end up scattering things around and hurt discoverability. There are other ways to do modelling that scale very well. There are a few blog posts on it, here's one from someone at Basecamp: https://dev.37signals.com/vanilla-rails-is-plenty/

      This is of course very OO which I'm not a huge fan of. Elixir's Phoenix framework, for example, uses "contexts" which is meant to group all related functionality. In short they could be considered a "facade."

      In any event, if you like services you like services, they can work, but saying MVC isn't enough for production-grade is a bit misguided.

      I do agree that model callbacks for doing heavy lifting business processes is not great, though for little things like massaging data into the correct shape is pretty nice.

      • jt2190 2 hours ago

        It would help a lot if you would clarify what you mean by “service object”. In my experience a single method on a service object would define a transaction. Is that what you mean by “service object”?

        • sodapopcan an hour ago

          Along the lines of what OP is talking about, part of the problem is that Rails has no service objects, so I have seen a handful of different ideas of what they mean (probably no more than 10).

          The one I've seen he most is stuff like `UserRegistrationService` or the like. These things can end up getting scattered and in general, I would rather just talk to the business object, ie, `User.register` which can delegate to a more complex private object handling it. It's basically "inverting" things. The win here is that things are more discoverable (You can look at the user object and see a list of everything it does) and more importantly draws better boundaries. This way the web layer only has to know about `User` instead of `RegisterUserService` and `DeleteUserService` etc.

          Again, services can work and aren't inherently bad, but plain MVC can certainly also work.

  • notamy 3 hours ago

    What an amazing name choice, certainly one way to end up at the top of search results :P

    To be serious, good job!! Building a good framework is a shockingly large task, and it’s always nice to see people exploring the design space and trying for new ideas.

    • alberth 24 minutes ago

      > What an amazing name choice

      "Row" would be another good name choice, that would also be easier to say than 'rwf'.

      RustOnWeb.com is even able to buy for $10 :)

      Just say'n

  • stackskipton 2 hours ago

    As SRE, I got interested in https://levkk.github.io/rwf/migrating-from-python/. On one hand, this is crazy neat you were able to pull it off. On the stability SRE hand, I'm internally screaming. At scale, this should be handled by Reverse Proxy (Caddy, Nginx, Traefik, whatever)

    • levkk 2 hours ago

      I thought the same thing, but this allows you to test your changes locally as an application engineer, without the back and forth. This goes back to the good old monolith vs. microservices debate.

      Writing a stable WSGI server is possible, and not very hard with a bit of attention to detail, e.g. thread counts, vacuum (just like good old php-fpm, restart every n requests...), etc. Basically if you implement most options uwsgi has, you're on the right path. It's on the roadmap to make Rwf comparable to running Gunicorn.

      • stackskipton 38 minutes ago

        Sure, but Gunicorn doesn't try and run Node. I totally get benefit for development; I was just worried about someone YOLOing this into production.

    • biorach 2 hours ago

      I imagine the author assumed a technical audience wouldn't need to be told of the necessity of a reverse proxy in front of the wsgi server

      • stackskipton an hour ago

        As SRE, you assume 100% wrong. Devs totally need to be told "Please don't do this in production." They will ignore you but hey, you might reach a few.

      • drcongo 2 hours ago

        As a Python dev, I imagined the same.

  • JodieBenitez 3 hours ago

    > (think Rails, not Flask)

    I like that... we need more (or better) opiniated frameworks a la rails/django in static languages.

  • apbytes 2 hours ago

    Great work!! I was just talking about how this is a major gap in Rust and here you are the very next day! Looking forward to use and contribute!

  • sea-gold 2 hours ago

    How does it compare with https://leptos.dev ?

  • fHr 2 hours ago

    Nice got rustpilled myself recently through ditching webpack js loaders and using rust ones which are 50x faster, rust is so preformance enhancing, c++ and rust are my favourite languages atm.

  • jpopesculian 2 hours ago

    Looks cool! How does it compare to loco.rs?

  • nwnwhwje 3 hours ago

    Well done! You could try to get mentioned on https://www.arewewebyet.org/

    • trevor-e 3 hours ago

      Very surprising this page doesn't mention loco.rs which seems like the most "Rails" Rust framework out there.

  • wormlord an hour ago

    Cool! Since I learned Rust I've wanted a Django replacement that has the functionality of a batteries included Web Framework + the speed/footprint of Rust. I'll check it out!

  • unjkyivbnp an hour ago

    Based! Django/Rails in a god tier language!

    my suggestions:

    - async-trait should be stabilized now, so you shouldn't need the macro anymore

    - Add opentelemetry integration so we get metrics and tracing out of the box

    - use jemalloc for linux targets

    Good work! Keep it up!

    • levkk an hour ago

      Thank you!

      I tried to use standard async traits, but they don't support dynamic dispatch [1] which Rwf uses extensively.

      I'll be adding opentelemetry tags to functions (from the `tracing` crate). jemalloc can be added to the binary apps that use Rwf, we don't need to add it as a dep to the lib.

      Cheers!

      [1] https://blog.rust-lang.org/2023/12/21/async-fn-rpit-in-trait...

  • donq1xote1 an hour ago

    I love rust!! This is so cool and I'm a beginner and I'm not sure if I can utilize this framework or not.

    • levkk an hour ago

      You definitely can. I remember learning Django and Rails as a beginner, it wasn't straight forward. New things are hard until they are not new. Good luck!

  • tommaho 3 hours ago

    Thanks for sharing!

    As a heads-up, The Pages documentation page is blank.

    https://levkk.github.io/rwf/controllers/pages/

  • MASNeo 2 hours ago

    I can already hear people asking "Did you aRWF already?" Seriously, the migration option is precisely how I think migration for years. Great job!

  • alberth 3 hours ago

    Does Rust have any DSL for web use (e.g. Rails in someways is a DSL to Ruby)?

    I ask because I imagine a simplified (Rust) syntax would be more inviting to newcomers.

    • levkk 3 hours ago

      Yup, they are called "macros". Rwf uses a few of them, some of which you'll find familiar, e.g. `render!` which returns a rendered template with HTTP 200 [1].

      [1] https://levkk.github.io/rwf/views/templates/templates-in-con...

      • alberth 2 hours ago

        That's good to know.

        From the ReadMe example, is there a way to use macros to simplify the following line of code:

          async fn handle(&self, request: &Request) -> Result<Response, Error> {
        
        I ask because many web developers don't come from a C/C++/Rust background - so the line above will be jarring/off-putting to many.

        (Awesome project btw)

        • afavour 2 hours ago

          Rust can be an intimidating language but the example you’ve provided there really shouldn’t be intimidating to anyone that’s using TypeScript today. There’s a little learning to with &self and & but that’s really basic Rust stuff. I don’t think it’s wise for a framework to attempt to hide core components of the language.

        • Scarblac 2 hours ago

          Coming from Typescript that doesn't look very ominous to me, though it would nice if the types could be inferred somehow.

        • fkyoureadthedoc 2 hours ago

          You might be surprised, with Typescript's ubiquity in the web space the type definitions probably won't be too scary. I've never used Rust but I assume `&` is some kind of Rusty pointer.

        • levkk 2 hours ago

          Thanks!

          Yes, I was thinking of adding a shorthand for that. Will add something soon!

  • the__alchemist 2 hours ago

    Love it; this is a big gap in Rust's ecosystem IMO.

    • culi 2 hours ago

      Is it? Asking as someone not very tuned into the ecosystem. Based on TechEmpower's Web Framework Benchmarks[0] and AreWeWebYet's resounding "yes!" for years now[1] I always got the impression that there were quite a few options available.

      Rocket, Actix, Axum, Salvo, etc just to name a few. Each with different focuses (e.g. performance vs "batteries-included-ness")

      [0] https://www.techempower.com/benchmarks/#hw=ph&test=composite...

      [1] https://www.arewewebyet.org/

      • dehrmann an hour ago

        I would have argued Rust isn't the right choice for a web framework unless the team is rust-first because the memory guarantees aren't really needed, and you're better off with occasional GC pauses and faster development velocity.

      • stackskipton 2 hours ago

        No, it's there but it's not popular and probably won't be for a while. Higher level languages like Java/JS/.Net/Go already do the job well enough for vast majority of use cases. Sure, there are cases like Discord where Go performance was impactful to their operations but those are pretty niche edge cases. Vast majority of people don't have those edge cases so any GC stutter is fine.

      • the__alchemist 2 hours ago

        Great question!

        The frameworks you listed are not a direct comparison to this lib, nor Rails, nor Django. They are Flask analogs. They are ideal for microservices, but are not a substitute for a batteries-included framework of the sort used in websites.

        I love rust, but don't use it for web backends because there is nothing on Django's level.

        • cchance 2 hours ago

          So... rust "on rails" is basically ... https://github.com/loco-rs/loco

          less rails is... leptos, and a few others

        • culi an hour ago

          Rocket comes with support for templating, cookies, websockets, middleware, an orm, testing, etc. I'm not familiar with Python web development (or why anyone would reach for Python for a webapp in 2024 :P), but it seems pretty analogous to Rails

          It's also the oldest/most mature tool out there

        • stackskipton 2 hours ago

          How many people are greenfield new Django style projects? I know Static Server-Side Rendering is becoming new hotness but I still thought pure Server-Side Rendering is frowned upon.

          Most of SSR I see is still SPA + Rest API/GraphQL backend with some scraper generating all the HTML.

          • the__alchemist 2 hours ago

            This is orthogonal; You don't use auth, email, automatic admin, migrations etc from a SPA; those are backend jobs.

  • sureglymop 3 hours ago

    Looks great, very interesting! How is the state of to documentation?

  • taikahessu 2 hours ago

    Impressive launch, good luck and happy coding!

  • giancarlostoro 3 hours ago

    Very interesting. I might have to check this out after work!

  • Havoc 3 hours ago

    Hopefully it takes off.

  • hkc88hkc 2 hours ago

    Well done!