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.
Install
Section titled “Install”cargo add nest-rs-server-timingMount it
Section titled “Mount it”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:
$ curl -i http://localhost:8080/items | grep -i server-timingserver-timing: total;dur=12.34That alone shows up in DevTools next to the request — round-trip vs. server time becomes a glance.
Record sub-steps
Section titled “Record sub-steps”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:
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)) }}$ curl -i http://localhost:8080/items | grep -i server-timingserver-timing: db;dur=7.82, render;dur=0.41;desc="wire transform", total;dur=8.96record(name, dur)— short entry;namemust be a valid header token (no spaces, no commas).record_with_desc(name, desc, dur)—descshows as a tooltip in DevTools; any string is allowed, the formatter quotes/escapes for you.
Going further
Section titled “Going further”- OpenTelemetry — for spans the frontend cannot see (cross-service traces, async work after the response, queue jobs).
- Interceptors — the seam
ServerTimingModuleplugs into; the same shape any other cross-cutting concern uses.
Reference
Section titled “Reference”crates/nest-rs-server-timing/—ServerTimingModule,Timings,Entry.