idParam
Call Signature
Section titled “Call Signature”idParam<
ParamKey,Brand>(paramName,codec,options?): (req,res,next) =>void
Defined in: src/adapters/express.ts:152
Express middleware that validates a named route param against a codec via safeParse.
Default (no options): calls next(err) with an IdParamError carrying status and reason,
so the app’s existing error-handling middleware controls rendering. The adapter does not write
a response body itself.
options.onError: when provided, the adapter calls the hook on validation failure. If
the hook sends a response (res.headersSent is true after it returns), the adapter takes
no further action. Otherwise — including if the hook calls next() instead of next(err) —
the adapter falls back to next(new IdParamError(...)), so the route handler never runs with
an invalid ID.
options.status: remaps the default HTTP status for a reason without a full handler.
- Brand mismatch (
invalid_prefix) →reason: "brand_mismatch", default 404 - Malformed or missing ID →
reason: "malformed", default 400
Storage: on success, stores the canonical Id<Brand> in res.locals[paramName] and calls next().
This contrasts with the Fastify adapter, which mutates request.params[paramName] in place.
Express writes to res.locals because it has no mutable request.params contract that is safe
to write to — req.params is populated by Express’s router and is not intended as a side-channel
for middleware output, while res.locals is the idiomatic per-request storage object.
Type Parameters
Section titled “Type Parameters”ParamKey
Section titled “ParamKey”ParamKey extends string
Brand extends string
Parameters
Section titled “Parameters”paramName
Section titled “paramName”ParamKey
IdVerifiableCodec<Brand>
options?
Section titled “options?”Returns
Section titled “Returns”(req, res, next) => void
Example
Section titled “Example”import { idParam, IdParamError } from "@smonn/ids/express";import { createTimestampId } from "@smonn/ids";
const usr = createTimestampId("usr");
// Default: forwards error to app error-handling middlewareapp.get("/users/:id", idParam("id", usr), (req, res) => { const id = res.locals.id; // Id<"usr">, canonical});
// Error-handling middleware receives the typed errorapp.use((err, req, res, next) => { if (err instanceof IdParamError) { res.status(err.status).json({ error: err.reason }); return; } next(err);});
// Override: consumer fully owns the responseapp.get("/orgs/:id", idParam("id", org, { onError: (failure, req, res) => res.status(failure.status).json({ error: failure.reason }),}), handler);
// Or a lightweight status remap without a full handlerapp.get("/things/:id", idParam("id", thing, { status: { brand_mismatch: 400 } }), handler);Call Signature
Section titled “Call Signature”idParam<
ParamKey,Brand>(paramName,codec,options?): (req,res,next) =>void
Defined in: src/adapters/express.ts:157
Express middleware that validates a named route param against a codec via safeParse.
Default (no options): calls next(err) with an IdParamError carrying status and reason,
so the app’s existing error-handling middleware controls rendering. The adapter does not write
a response body itself.
options.onError: when provided, the adapter calls the hook on validation failure. If
the hook sends a response (res.headersSent is true after it returns), the adapter takes
no further action. Otherwise — including if the hook calls next() instead of next(err) —
the adapter falls back to next(new IdParamError(...)), so the route handler never runs with
an invalid ID.
options.status: remaps the default HTTP status for a reason without a full handler.
- Brand mismatch (
invalid_prefix) →reason: "brand_mismatch", default 404 - Malformed or missing ID →
reason: "malformed", default 400
Storage: on success, stores the canonical Id<Brand> in res.locals[paramName] and calls next().
This contrasts with the Fastify adapter, which mutates request.params[paramName] in place.
Express writes to res.locals because it has no mutable request.params contract that is safe
to write to — req.params is populated by Express’s router and is not intended as a side-channel
for middleware output, while res.locals is the idiomatic per-request storage object.
Type Parameters
Section titled “Type Parameters”ParamKey
Section titled “ParamKey”ParamKey extends string
Brand extends string
Parameters
Section titled “Parameters”paramName
Section titled “paramName”ParamKey
IdCodec<Brand>
options?
Section titled “options?”Returns
Section titled “Returns”(req, res, next) => void
Example
Section titled “Example”import { idParam, IdParamError } from "@smonn/ids/express";import { createTimestampId } from "@smonn/ids";
const usr = createTimestampId("usr");
// Default: forwards error to app error-handling middlewareapp.get("/users/:id", idParam("id", usr), (req, res) => { const id = res.locals.id; // Id<"usr">, canonical});
// Error-handling middleware receives the typed errorapp.use((err, req, res, next) => { if (err instanceof IdParamError) { res.status(err.status).json({ error: err.reason }); return; } next(err);});
// Override: consumer fully owns the responseapp.get("/orgs/:id", idParam("id", org, { onError: (failure, req, res) => res.status(failure.status).json({ error: failure.reason }),}), handler);
// Or a lightweight status remap without a full handlerapp.get("/things/:id", idParam("id", thing, { status: { brand_mismatch: 400 } }), handler);