Skip to content

Server-Timing

Server-Timing is a W3C header browsers render in their Network panel (DevTools → Network → Timing tab) — a per-request breakdown of where the time went. It rides on the HTTP transport as an interceptor: importing ServerTimingModule attaches one that always appends a final total;dur=<ms>; handlers add their own sub-steps by recording into a per-request Timings accumulator pulled from the request extensions.

This is observability for the frontend developer’s browser, not a replacement for proper tracing — for cross-service spans and aggregations, reach for OpenTelemetry.

Terminal window
cargo add nest-rs-server-timing
apps/api/src/module.rs
use nest_rs_core::module;
use nest_rs_server_timing::ServerTimingModule;
#[module(imports = [ServerTimingModule])]
pub struct ApiModule;

Every response now carries a Server-Timing header. The minimum, unstructured form is one entry:

Terminal window
$ curl -i http://localhost:8080/items | grep -i server-timing
server-timing: total;dur=12.34

That alone shows up in DevTools next to the request — round-trip vs. server time becomes a glance.

Pull Timings out of the request extensions and call record (or record_with_desc) with a duration. The interceptor concatenates every recorded entry into the final header, with total appended on the way out:

src/items/http/controller.rs
use std::time::Instant;
use nest_rs_http::{controller, routes};
use nest_rs_server_timing::Timings;
use poem::Request;
#[controller(path = "/items")]
pub struct ItemsController {
#[inject]
svc: Arc<ItemsService>,
}
#[routes]
impl ItemsController {
#[get("/")]
async fn list(&self, req: &Request) -> Result<Json<Vec<Item>>> {
let timings = req.extensions().get::<Timings>().cloned().unwrap_or_default();
let started = Instant::now();
let rows = self.svc.list().await?;
timings.record("db", started.elapsed());
let started = Instant::now();
let view: Vec<Item> = rows.iter().map(Item::from).collect();
timings.record_with_desc("render", "wire transform", started.elapsed());
Ok(Json(view))
}
}
Terminal window
$ curl -i http://localhost:8080/items | grep -i server-timing
server-timing: db;dur=7.82, render;dur=0.41;desc="wire transform", total;dur=8.96
  • record(name, dur) — short entry; name must be a valid header token (no spaces, no commas).
  • record_with_desc(name, desc, dur)desc shows as a tooltip in DevTools; any string is allowed, the formatter quotes/escapes for you.
  • OpenTelemetry — for spans the frontend cannot see (cross-service traces, async work after the response, queue jobs).
  • Interceptors — the seam ServerTimingModule plugs into; the same shape any other cross-cutting concern uses.
  • crates/nest-rs-server-timing/ServerTimingModule, Timings, Entry.