Closed prabirshrestha closed 8 months ago
Thank you for your feedback on salvo-cli. I agree, it currently offers quite basic functionalities and not provide continual support for projects as effectively . I appreciate your suggestion to look into loco and its features inspired by Rails. I appreciate your support and look forward to potentially collaborating with you. Have a great day! :)
Here a few things I suggest could help improve the salvo-cli in random order.
cargo run
. For example I had to read the code to understand where the swagger ui is.rand_utils.rs
considering using specific files such as password.rs
.get_user_page_router
(for html), get_user_route
(for apis), delete_user_route
and so on. Probably worth having something like https://pocketbase.io or https://staticbackend.com and build upon it or just use their schema.
Thank you for every suggestion, you have made a lot of valuable comments from an experienced programmer's perspective, I will prioritize them by my perspective and send them out to be done and optimized little by little. I have some questions for you.
By the way, I'm a bit busy at work right now, even working 13 hours a day! I'll keep updating quickly when I can. It's a long way to go. Thanks again for the advice!
My thoughts on sql/db/orm is that salvo-cli
only supports one but allows us to bring our own. Here is how I would do it. I will give an example on how one could implement sign up in a generic way. (Pasting code from my old project for reference)
#[async_trait]
pub trait UserService: Sync + Send {
async fn signup(&self, new_user: &CreateUserRequest) -> AppResult<CreateUserResponse>;
}
todo!()
. I can use this with surrealdb or any other implantation. #[async_trait]
impl UserService for MyUserService {
async fn signup(&self, new_user: &CreateUserRequest) -> AppResult<CreateUserResponse> {
todo!();
}
}
sqlx
but use AnyPool
and sea-query-binder
. This will allow you to write sql once but use it for sqlite, mysql or Postgres.sqlx = { version = "0.6.1", default-features = false, features = ["any", "sqlite", "mysql", "postgres"] }
sea-query = "0.27.1"
sea-query-binder = { version = "0.2.1", features = ["sqlx-any", "sqlx-postgres", "sqlx-mysql", "sqlx-sqlite"] }
As for ORMs I'm not a fan of it. While it allows us to move fast initially and makes it easy to refactor in the long run we always land of fighting ORMs. The good part about the solution is one can use custom sql if needed that might be specific to your database and take advantage of it.
As for surrealdb it is quite new. Due to popularity of sql I would suggest supporting sqlx first. Once the cli has stable list of features, it then would make it easy to port to surrealdb if needed. For now I'm ok with just todo!()
implementation so I can easily replace it on my side.
For background jobs I use my own https://github.com/prabirshrestha/mq. Currently only Surrealdb backend is implemented. One could create own backend using sql or Redis or cloud queues if needed.
I don't expect this to be done soon anytime, but wanted to just share thoughts as I think there is lot of potential for the cli to make it easy. Every time I create a new salvo project I'm doing quite a lot of copy paste so I think it not just me but others could also benefit here. Best of luck at your work!
My thoughts on sql/db/orm is that
salvo-cli
only supports one but allows us to bring our own. Here is how I would do it. I will give an example on how one could implement sign up in a generic way. (Pasting code from my old project for reference)
- Introduce traits allowing easy change of implementations.
#[async_trait] pub trait UserService: Sync + Send { async fn signup(&self, new_user: &CreateUserRequest) -> AppResult<CreateUserResponse>; }
- If I choose bring your own, replace with
todo!()
. I can use this with surrealdb or any other implantation.#[async_trait] impl UserService for MyUserService { async fn signup(&self, new_user: &CreateUserRequest) -> AppResult<CreateUserResponse> { todo!(); } }
- As for database I suggest to go with
sqlx
but useAnyPool
andsea-query-binder
. This will allow you to write sql once but use it for sqlite, mysql or Postgres.sqlx = { version = "0.6.1", default-features = false, features = ["any", "sqlite", "mysql", "postgres"] } sea-query = "0.27.1" sea-query-binder = { version = "0.2.1", features = ["sqlx-any", "sqlx-postgres", "sqlx-mysql", "sqlx-sqlite"] }
DB using
AnyPool
Migration now has to move to rust, but you write once and it works for sqlite, mysql and Postgres. Queries are also generic that works in sqlite, mysql and postgres. This is what I use for migrator. As for ORMs I'm not a fan of it. While it allows us to move fast initially and makes it easy to refactor in the long run we always land of fighting ORMs. The good part about the solution is one can use custom sql if needed that might be specific to your database and take advantage of it.As for surrealdb it is quite new. Due to popularity of sql I would suggest supporting sqlx first. Once the cli has stable list of features, it then would make it easy to port to surrealdb if needed. For now I'm ok with just
todo!()
implementation so I can easily replace it on my side.For background jobs I use my own https://github.com/prabirshrestha/mq. Currently only Surrealdb backend is implemented. One could create own backend using sql or Redis or cloud queues if needed.
I don't expect this to be done soon anytime, but wanted to just share thoughts as I think there is lot of potential for the cli to make it easy. Every time I create a new salvo project I'm doing quite a lot of copy paste so I think it not just me but others could also benefit here. Best of luck at your work!
Thank you for your reply, I will keep learning and writing better tools. Have a great day! :)
[ ] use cargo workspace
[ ] add support for SPA so client side apps can be written.
[ ] Add support for surrealdb. It can be embedded as well as run in clusters, supports vector embeddings
[x] use yaml for config, as this allows other folks in the team not familiar with rust or toml.
[x] generate README.md so we know what the next step is. for now it probably doesn't matter as it just cargo run. For
example I had to read the code to understand where the swagger ui is.
[ ] add support for background jobs. I think this is very important to enable rich responsive servers.
[ ] add support for cron jobs
[ ] instead of rand_utils.rs considering using specific files such as password.rs.
[ ] Considering using traits for Services. This would allow us to change the implementation via config.
[ ] consider consistent naming for routes such as get_user_page_router (for html), get_user_route (for apis), - [ ] delete_user_route and so on.
[ ] remove demo.rs
[ ] considering using sub folders for templates.
[ ] Add support for sending emails. Emails usually have html templates too.
[ ] Add support for 2fa auth.
[ ] Add support for App passwords.
[ ] Add support for updating/reseting passwords.
[x] call it open api instead of swagger.
[ ] add support for login, logout and register in homepage.
[ ] Add generic pagination concept.
[ ] add CSRF
[ ] add localization
[ ] add systemic and watch support so saving .rs files would automatically build
[ ] add support for server errors. might be even show stack traces in debug mode?
[ ] socket.io with https://github.com/Totodore/socketioxide for real time communication
[ ] in memory as well as distributed caching
[ ] generic storage using https://opendal.apache.org/. Allowing to easily swap files from files system to cloud storages.
Another one I would consider adding is watch mode.
This will allow us to do something like this and when it is compiling the http endpoint will be available and wait for the compilation to finish and start the server.
cargo install systemfd
systemfd --no-pid -s http::8080 -- cargo watch -x 'run'
You can see the sample from here.
Recently published a surrealdb-migrator that I have been using internally in one of my project. This should hopefully unblock surrealdb support.
Here are few more:
One option to support errors is preserve the state either in query string or use flash as shown here. This will require full page refresh.
If you really want to include some sort of dynamic UI, probably worth using htmx/unploy/alipne (currently) but without other JS files or scripts. I personally haven't used any of these so can't give any preferences, but would use one that has the least size and has good set of features.
Group templates into folder. Such as as templates/admin/users/list_page.html
instead of templates/user_list_page.html
.
Implement 500 internal server error page.
Add support for CSS minification/compliation. Might be can use grass which includes support for compiling SASS files so we get benefits for variables and imports that can be merged to a single file.
static CSS: &str = grass::include!("../static/_index.scss");
cors_allow_origin:
- "https://salvo.rs"
(If it makes it easy for you to create an new issue with checkmarks as the main comment feel free to do so and I can start commenting there instead and you can lock this issue.)
Here are few more:
- Avoid adding JS library if possible. Most likely folks using will use a full blown SPA libraries such as React/Vue/Angular and so on, so I wouldn't bother. Another reason to do so if some may want apps to work with JS disabled specially for non authenticated pages. JS also may be no go for those who use Typescript and have large number of engineers contributing.
One option to support errors is preserve the state either in query string or use flash as shown here. This will require full page refresh.
If you really want to include some sort of dynamic UI, probably worth using htmx/unploy/alipne (currently) but without other JS files or scripts. I personally haven't used any of these so can't give any preferences, but would use one that has the least size and has good set of features.
- Group templates into folder. Such as as
templates/admin/users/list_page.html
instead oftemplates/user_list_page.html
.- Implement 500 internal server error page.
- Add support for CSS minification/compliation. Might be can use grass which includes support for compiling SASS files so we get benefits for variables and imports that can be merged to a single file.
static CSS: &str = grass::include!("../static/_index.scss");
- Use layout pages instead of duplicating code.
- Don't default cors to the following. could be security issue.
cors_allow_origin: - "https://salvo.rs"
(If it makes it easy for you to create an new issue with checkmarks as the main comment feel free to do so and I can start commenting there instead and you can lock this issue.)
Thanks for the added advice; I'll start a new issue!
Seems like salvo-cli is quite basic. Every time I write a new salvo project I copy paste lot of existing code. loco looks pretty great but is for Axum. It is inspired by Rails so has lot of ideas figured out already.
Would be more than happy to help out as necessary.