- Set up the
.env
by following the.env.example
file - Setting up the database
- Spin up the database with
docker compose up
- Install
sqlx-cli
withcargo install sqlx-cli
- Create the tables with
sqlx migrate run
- Enable sqlx building in "offline mode" with
cargo sqlx prepare
- Spin up the database with
- Run the project with
cargo run
Note: Check
makefile
for more runnable commands
- Create a migration file with
sqlx migrate add -r <migration_name>
- Write the up migration in the generated
.up.sql
file - Write the down migration in the generated
.down.sql
file - Run the migration with
sqlx migrate run
- (Optional) Rollback the migration with
sqlx migrate revert
It speeds up the development process by automatically recompiling the project when a file changes.
- Specify the files to be watched in
tailwind.config.js
- Configure the
input.css
needed bytailwindcss
at./assets/input.css
- Include the
./assets/output.css
to be compiled in the header ofbase.html
- Run
npx tailwindcss -i ./assets/input.css -o ./assets/output.css --watch
Method 1 (with browser reload) (Recommended)
- Install
cargo-watch
withcargo install cargo-watch
- Use the crate
tower-livereload
to enable live reload - Run the project with
cargo watch -x run
Method 2 (with browser buffer)
- Install
cargo-watch
withcargo install cargo-watch
- Install
systemfd
withcargo install systemfd
- Use the crate
listenfd
to enable buffer - Run the project with
systemfd --no-pid -s http::3000 -- cargo watch -x run
Note: the methods are mutually exclusive
- Install TailwindCSS with
npm install -D tailwindcss
- Install TailwindCSS plugins with
npm install -D @tailwindcss/forms @tailwindcss/typography @tailwindcss/aspect-ratio @tailwindcss/container-queries
Normally htmx don't swap a div under certain response status.
- 204 No Content by default does nothing, but is not an error
- 2xx, 3xx and 422 responses are non-errors and are swapped
- 4xx & 5xx responses are not swapped and are errors
- all other responses are swapped using "..." as a catch-all
However, it can be altered by a htmx extension called response-targets to swap the div.
why always defining trait for handlers, services, and pages?
because fundamentally most of these api functions are stateless,
we don't actually have a instance storing its own state
so Self::create_router would still be better than
a separated fn create_router(service: impl HasCatalogHandlers) -> Router<AppState>;
in that we don't need to pass a instance of service to the every functions,
a associated type would be sufficient and reduce code duplication.
Moreover, there are many common behaviors that should be shared between layers and inside the same layer. It provides a convenient way to reuse the shared behaviors through trait methods or default implementations in the same trait or in the associated type which requires a trait bound. It also ensures that every new struct is enforced to implement the same behaviors before being introduced.