- Rwf est un framework complet pour créer des applications web en Rust, basé sur le modèle classique MVC, et fournit par défaut les fonctionnalités nécessaires pour construire des apps web rapides et sûres
- Les fonctionnalités de base incluent un serveur HTTP, un ORM pour les requêtes PostgreSQL, des templates dynamiques, l’authentification et les sessions utilisateur, les middlewares, les tâches en arrière-plan et planifiées, ainsi que les migrations de base de données
- Il propose aussi un framework REST intégré avec prise en charge de la sérialisation JSON, WebSockets, l’hébergement de fichiers statiques, la configuration par environnement, la journalisation et les métriques, ainsi qu’une CLI
- Il s’intègre étroitement à Hotwired Turbo pour créer des SPA pilotées par le backend, et fournit un serveur WSGI pour migrer depuis Django/Flask ainsi qu’un serveur Rack pour migrer depuis Rails
- Actuellement en bêta, le projet recherche des early adopters ; il indique que la plupart des fonctionnalités sont en bon état et que la documentation est utilisable
Framework web MVC pour Rust
- Rwf est un framework complet pour construire des applications web en Rust
- Il est écrit selon le modèle classique MVC(model-view-controller)
- Son objectif est d’inclure par défaut les fonctionnalités nécessaires pour créer facilement des apps web rapides et sûres
- La documentation est disponible dans la documentation officielle
Fonctionnalités proposées
- Serveur et traitement des requêtes
- Données et structure applicative
- Fournit un ORM convivial pour créer facilement des requêtes PostgreSQL
- Prend en charge les migrations de base de données
- Fournit des Dynamic templates
- Inclut l’Authentication et des sessions utilisateur intégrées
- API et traitement des tâches
- Outils d’exploitation et de développement
- Prend en charge la configuration par environnement
- Fournit la journalisation et les métriques
- Inclut une CLI
Prise en charge de Hotwired Turbo et des migrations
Démarrage rapide
- Créez une application binaire Rust et ajoutez
rwf comme dépendance pour l’utiliser
cargo add rwf
- Une app de base utilise
rwf::prelude::* et rwf::http::Server pour définir les contrôleurs et les routes
use rwf::prelude::*;
use rwf::http::Server;
#[controller]
async fn index() -> Response {
Response::new().html("Welcome to Rwf!
")
}
#[tokio::main]
async fn main() {
Server::new(vec![
route!("/" => index),
])
.launch()
.await
.unwrap();
}
Exemples et état du projet
- Les cas d’usage courants sont disponibles dans les examples
- Rwf est actuellement en bêta et recherche des early adopters
- Le projet indique que la plupart des fonctionnalités sont en bon état et que la documentation est utilisable
- Les contributions sont les bienvenues ; le guide se trouve dans CONTRIBUTING, la présentation de la structure du code dans ARCHITECTURE, et la liste des fonctionnalités souhaitées dans ROADMAP
1 commentaires
Commentaires sur Hacker News
Le lancement semble assez irréel, félicitations. La plus grande leçon que j’ai apprise en faisant du MVC web avec Rails pendant longtemps, c’est qu’un framework doit fortement décourager la logique métier dans la couche modèle
Je pense qu’il vaut mieux éviter de mettre dans le modèle de données des callbacks Active Record, par exemple des hooks comme
afterCreate. Même si ce n’est pas encore dans l’ORM mais que c’est sur la feuille de route, je pense qu’il vaudrait mieux ne pas l’ajouterCela dit, ça ne fonctionne vraiment que si l’on encourage fortement une couche service, c’est-à-dire une couche de logique métier. Dans la plupart de mes apps Rails, j’utilise des gems comme Interactor pour construire des objets commande/service
À mon avis, avec le seul MVC, et donc avec la configuration par défaut de Rails, on n’est pas dans une situation idéale pour écrire une application exploitable en production, à cause de l’absence de couche service. Et je me demande aussi pourquoi ne pas avoir utilisé les ORM ou query builders existants de la communauté. Je n’ai pas encore écrit une seule ligne de Rust, mais ça m’intrigue de plus en plus
Les services devraient presque tous être des fragments de code plus complexes et plus spécifiques déclenchés depuis les modèles. Des contrôleurs fins et des modèles épais ont été ce qu’il y avait de mieux pour le débogage, l’organisation et la découvrabilité, alors que l’usage intensif de services a souvent produit, dans mon expérience, beaucoup de code spaghetti
Même d’un point de vue purement orienté objet, le modèle est l’objet de base qui définit l’entité.
Userdoit tout savoir de ce qu’il doit savoir sur lui-même, et communiquer avec les autres entités par messagesEn revanche, je suis d’accord au sujet des callbacks. Les callbacks créent beaucoup d’effets de bord bizarres et rendent vraiment le débogage difficile
C’est possible, mais cela rend assez difficile de raisonner sur les manières dont une entité peut être mise à jour. N’importe quel nombre de procédures peut la modifier, chacune avec ses propres règles métier. À ce compte-là, autant laisser les procédures écrire directement en base de données et supprimer les modèles
Mais après plus de 20 ans de développement, j’en suis arrivé à la conclusion que l’encapsulation est une charge, pas une fonctionnalité, et que les données doivent être séparées des opérations effectuées sur elles. On appelle cela la conception orientée données ou la programmation orientée données, et je ne suis clairement pas le seul à être arrivé à cette conclusion
Il existe d’autres approches de modélisation qui passent bien à l’échelle, et il y a quelques articles à ce sujet. Il y en a un écrit par quelqu’un de chez Basecamp : https://dev.37signals.com/vanilla-rails-is-plenty/
Bien sûr, c’est très orienté objet, donc je n’en suis pas non plus un grand fan. Par exemple, le framework Phoenix d’Elixir utilise des contexts pour regrouper les fonctionnalités liées, ce qu’on peut voir, en simplifiant, comme des façades
Si vous aimez les services, vous pouvez en utiliser, mais dire que le MVC ne suffit pas pour de la production me semble un peu à côté de la plaque. Traiter de lourds processus métier dans des callbacks de modèles n’est pas une bonne idée, mais pour de petites opérations qui remettent les données dans la bonne forme, c’est tout à fait correct
Il est possible que je sois biaisé, surtout parce que mon expérience en programmation a commencé avec du PHP pour WordPress, où les hooks sont omniprésents
Ce que j’ai construit dispose de spécifications de tables capables de se convertir en définitions SQL ou de produire des formulaires HTML joliment formatés. Un contrôleur séparé gère toutes les connexions à la base et les opérations CRUD, et il y a des hooks avant/après qui permettent, si nécessaire, de référencer facilement les contraintes d’autres tables
Ça fonctionne plutôt bien, mais si je le pouvais, je passerais immédiatement à Laravel
Pour un framework REST, je m’attendrais par défaut à la génération de définitions Swagger/OpenAPI. Pour moi, c’est l’une des fonctionnalités décisives de FastAPI
Je comprends aussi mal pourquoi ils ont créé leur propre ORM au lieu de s’intégrer à un ORM existant comme diesel.rs, et pourquoi ils ont créé leur propre langage de templates au lieu de choisir un moteur de templates populaire existant
À part ça, le projet a l’air vraiment intéressant et je vais continuer à le suivre
[0] https://diesel.rs/
[1] https://crates.io/categories/template-engine
Globalement, c’était une bonne première tentative pour faire correspondre les types de base de données aux types Rust, mais un ORM doit être plus flexible et plus agréable à utiliser. Il doit rendre les requêtes plus faciles à écrire, pas plus difficiles :)
Pour les templates, créer son propre langage tient presque du rite de passage dans le club des nerds de plus de 30 ans. Je n’ai pas lu le Dragon Book, mais j’ai toujours voulu suivre ce cours à l’école. Il existe toujours plusieurs implémentations pour une même idée, et j’ai eu le sentiment qu’il était juste d’utiliser une implémentation qui imite d’assez près ce que fait ERB dans Rails
Du point de vue d’un SRE, https://levkk.github.io/rwf/migrating-from-python/ a attiré mon attention. D’un côté, ce qu’ils ont réussi à faire est vraiment impressionnant, mais du point de vue SRE, centré sur la stabilité, ça donne envie de crier intérieurement
À grande échelle, c’est le genre de chose qui devrait être géré par un reverse proxy comme Caddy, Nginx ou Traefik
Il est possible d’écrire un serveur WSGI stable, et ce n’est pas si difficile si on fait un minimum attention à des détails comme le nombre de threads, le vacuum, ou un redémarrage tous les n requêtes comme l’ancien php-fpm. En gros, si on implémente la plupart des options de uwsgi, on est sur la bonne voie. Rendre Rwf comparable à une exécution avec Gunicorn fait partie de la roadmap
Le choix du nom est vraiment remarquable. C’est une façon d’arriver tout en haut des résultats de recherche :P
Plus sérieusement, c’est du beau travail. Créer un bon framework représente une quantité de travail étonnamment importante, et c’est toujours agréable de voir des gens explorer l’espace de conception et essayer de nouvelles idées
rwfRustOnWeb.com est aussi disponible pour 10 $ :) je dis ça comme ça
J’aime bien la formule « pensez Rails, pas Flask ». Les langages à typage statique auraient besoin de plus de frameworks d’opinion à la Rails/Django, ou de meilleurs frameworks de ce type
C’est bien fait. Ça vaudrait le coup d’essayer de le faire référencer sur https://www.arewewebyet.org/
Sympa ! Un Django/Rails écrit dans le meilleur des langages
À titre de suggestion,
async-traitest maintenant peut-être stabilisé, donc le macro n’est peut-être plus nécessaire, et ce serait bien d’ajouter une intégration OpenTelemetry pour avoir par défaut des métriques et du tracing. jemalloc serait aussi une bonne idée pour LinuxJe compte ajouter des tags OpenTelemetry aux fonctions du crate
tracing. Pour jemalloc, il peut être ajouté aux applications binaires qui utilisent Rwf, sans qu’il soit nécessaire de l’inclure comme dépendance de la bibliothèque[1] https://blog.rust-lang.org/2023/12/21/async-fn-rpit-in-trait...
J’ai essayé d’apprendre Rust, mais j’ai abandonné assez tôt. En venant de C#, C/C++, Java et Python, Rust m’a paru étrange
Ce n’est pas l’étrangeté en elle-même qui m’a fait renoncer, mais le fait que la vitesse d’exécution d’une tâche y soit plus lente que dans des langages comme C#, Java ou Python
Ces temps-ci, je fais surtout du développement web, donc la facilité d’usage et la productivité passent avant tout. Rust est peut-être un peu meilleur en débit et en vitesse, mais quand on regarde les benchmarks récents de frameworks web, C# et Java s’en sortent eux aussi très bien sur ce point
Je ne voudrais pas que cela soit lu comme une critique de Rust ou comme une manière de minimiser le grand accomplissement que représente la création d’un framework MVC from scratch. C’est plutôt dans l’esprit du « bon outil pour le bon travail »
Il faut une forte motivation pour franchir la courbe d’apprentissage. Il faut avoir une raison précise : devoir utiliser le langage, être attiré par les avantages de son système de types et de sa sécurité, ou toute autre raison personnelle concrète
Sans cette motivation, il n’y a vraiment aucun problème. Moi non plus, je ne l’avais pas au départ, et c’est pour ça que mes premières tentatives d’apprentissage ne sont pas allées bien loin. Comme beaucoup de gens l’ont constaté, Rust demande plus de temps que Java ou Python pour faire avancer les choses, et oblige à réfléchir davantage en amont. Dans certaines situations, cela en vaut la peine, mais si on n’a pas une idée claire de la raison pour laquelle on accepte ce compromis, il n’y a pas de raison forte de persévérer
J’ai moi aussi commencé à aimer Rust récemment après avoir abandonné le loader JS de webpack pour un loader Rust 50 fois plus rapide. Rust apporte des gains de performance très visibles, et aujourd’hui C++ et Rust sont mes langages préférés
J’ai évalué tous les candidats généralement cités pour du backend Rust et j’ai même réalisé de petits prototypes. Jusqu’à présent, bien que ce soit plus bas niveau que ce que je souhaiterais, j’en suis venu à accepter à contrecœur la conclusion collective selon laquelle Axum est globalement le meilleur choix
Les autres candidats étaient Loco et Rocket. Loco ressemblait trop à Rails, et Rocket a commencé à me gêner à cause de ses macros à mesure que j’utilisais davantage Rust
Ce framework semble correspondre parfaitement à mon critère de « batteries incluses, sans être trop prescriptif ». C’est un bon ajout à l’écosystème