Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions docs/content/blog/2026-02-22-rapina-0-6-0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
+++
title = "Rapina 0.6.0"
description = "Route auto-discovery, Prometheus metrics, and security hardening"
date = 2026-02-22

[taxonomies]
categories = ["release-notes"]
tags = ["release", "routing", "metrics", "prometheus", "security", "ci"]

[extra]
author = "uemuradevexe"
+++

Rapina 0.6.0 shipped on February 22, 2026. The headline feature was route auto-discovery: handlers annotated with `#[get]`, `#[post]`, `#[put]`, or `#[delete]` registered themselves at link time via `inventory`, removing the need to wire routes manually in `main.rs`. The release also added a Prometheus metrics endpoint and a round of security and CI hardening.

---

## Route auto-discovery

Before 0.6.0, every route had to be registered explicitly:

```rust
let router = Router::new()
.get("/users", list_users)
.post("/users", create_user)
.get("/users/:id", get_user);

Rapina::new().router(router).listen("127.0.0.1:3000").await
```

With auto-discovery, calling `.discover()` was enough. The handler macros emitted an `inventory::submit!` at link time, and Rapina collected them automatically at startup:

```rust
use rapina::prelude::*;

#[get("/users")]
async fn list_users() -> &'static str { "[]" }

#[post("/users")]
async fn create_user() -> StatusCode { StatusCode::CREATED }

#[tokio::main]
async fn main() -> std::io::Result<()> {
Rapina::new()
.discover()
.listen("127.0.0.1:3000")
.await
}
```

Discovery was additive — `.router()` and `.discover()` worked together. Routes registered manually co-existed with discovered ones.

### Public routes

The `#[public]` attribute worked in both orderings with discovery enabled. Placing it above or below the route macro both marked the route as accessible without authentication, with no extra configuration:

```rust
// Both orderings are equivalent
#[public]
#[get("/health")]
async fn health() -> &'static str { "ok" }

#[get("/ping")]
#[public]
async fn ping() -> &'static str { "pong" }
```

At startup, Rapina logged the number of discovered routes and how many were public.

---

## Prometheus metrics

A `/metrics` endpoint was added as an optional feature. It was enabled with the `metrics` feature flag and a single builder call:

```toml
[dependencies]
rapina = { version = "0.6.0", features = ["metrics"] }
```

```rust
Rapina::new()
.with_metrics(true)
.router(router)
.listen("127.0.0.1:3000")
.await
```

The endpoint returned metrics in Prometheus text format. Three metrics were collected automatically:

| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `http_requests_total` | Counter | `method`, `path`, `status` | Total completed HTTP requests |
| `http_request_duration_seconds` | Histogram | `method`, `path` | Request duration in seconds |
| `http_requests_in_flight` | Gauge | — | Requests currently being processed |

### Path normalisation

To prevent label cardinality explosion, pure-numeric path segments were replaced with `:id` in metric labels:

| Raw request | Label |
|-------------|-------|
| `/users/42` | `/users/:id` |
| `/users/123/posts/456` | `/users/:id/posts/:id` |
| `/users/profile` | `/users/profile` |

Requests to `/users/1`, `/users/2`, and `/users/999` all mapped to the same label set.

---

## Security hardening

The repository received a set of security and supply-chain hardening changes:

- **`SECURITY.md`** — a security policy was added with a contact address for responsible disclosure.
- **`deny.toml`** — `cargo-deny` was added with rules for license compliance, duplicate dependencies, and known advisories.
- **`audit.yml`** — a new CI workflow ran `cargo audit` on every push to check for security advisories.
- **`scorecard.yml`** — [OpenSSF Scorecard](https://securityscorecards.dev/) was added to track supply-chain security practices.
- **`CODEOWNERS`** — a `CODEOWNERS` file established code review ownership.
- **`dependabot.yml`** — Dependabot was configured for both Cargo and GitHub Actions dependencies.

The CI workflow was also hardened: steps gained pinned permissions, and the release workflow was updated to separate build and publish steps.

---

## Community tooling

Three GitHub Actions workflows were added to improve the contributor experience:

- **Auto-labeler** — pull requests were automatically labelled by path using `labeler.yml`. Changes under `docs/` received a `docs` label, changes under `.github/` a `ci` label, and so on.
- **Welcome bot** — first-time contributors received a welcome comment when their first issue or pull request was opened.
- **`/release` command** — maintainers could trigger the release workflow with a `/release` comment on a pull request.

---

## Dependency updates

- `toml` was bumped to `1.0.0+spec-1.1.0`, bringing TOML spec 1.1 support. TOML 1.1 adds Unicode escape sequences and allows more characters in bare keys.

---

Upgrade by bumping the version in your `Cargo.toml`:

```toml
rapina = "0.6.0"
```
Loading