- Use `ApiError` as the error type for API routes - The return type of an HTTP route should not be `HttpResponse` if possible; always prefer more specific types - Use `web::Json` for JSON-encoded response - Use `()` for no content - Prefer `ApiError` variants: - `ApiError::Request` instead of `ApiError::InvalidInput` - `ApiError::Auth` instead of `ApiError::CustomAuthentication` - `ApiError::Internal` for database errors, 3rd party service errors, anything else internal - Use `eyre!` to construct a value for `Internal`, `Request`, and `Auth` variants - Error messages (both for errors and exceptions) must be formatted as per the Rust API guidelines: - lowercase message - no trailing punctuation - wrap code items e.g. type names in backticks - Prefer `wrap_internal_err`, `wrap_request_err` when attaching context to an existing error (like Anyhow `context` or Eyre `wrap_err`) - Prefer importing `eyre::Result` and using `Result` instead of `eyre::Result` - Prefer `eyre::Ok(value)` instead of `Ok::<_, eyre::Report>(value)` when an explicit Eyre result type is needed - All operations should ideally have some context attached - Database operations can have a message like `.wrap_internal_err("fetching XYZ")` - You can perform real-time queries against the databases in the Docker Compose - `docker exec labrinth-postgres psql -c "select 1"` - `docker exec labrinth-redis redis-cli flushall` - `docker exec labrinth-clickhouse clickhouse-client "select 1"` - On some machines, you may have to use `podman` instead of `docker` - check which one is available first - Hardcoded credentials for admin: - `Authorization: Bearer mra_admin` for default admin user - `Authorization: Bearer mra_user` for a regular user - `Modrinth-Admin: feedbeef` as admin key - If some steps require you to create a project/mod or version for testing, ask the user to go into the web frontend and manually create a project/version - When using `sqlx::query` etc. always use the macro form like `sqlx::query!` or `sqlx::query_scalar!` - never the plain function form. Avoid using `query_as!`. - Do not use `()` as an error type for operations, unless you have a very good reason. Either make a new error type, or use `eyre::Report`. - Do not run `cargo test`, even for a single specific test, unless explicitly prompted to by the user, since it takes a long time to run. - You can force a search reindex by: - Running `cd apps/labrinth && cargo run -p labrinth -- --run-background-task index-search` (prefer this if backend is running locally) - Hitting the force reindex admin endpoint - To seed the database locally: `psql postgresql://labrinth:labrinth@localhost/labrinth -f apps/labrinth/fixtures/labrinth-seed-data-202508052143.sql` - When writing `sqlx` queries, prefer `r#` raw strings over escaping quotes - When interacting with the Postgres database, prefer using a `ro_pool: ReadOnlyPgPool` when performing read-only operations - After writing a SQL query, run `EXPLAIN ANALYZE` to see how long the query would take to run. Report to the user the estimated performance impact of the query.