Skip to content

idParam

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.

ParamKey extends string

Brand extends string

ParamKey

IdVerifiableCodec<Brand>

IdParamVerifyOptions

(req, res, next) => void

import { idParam, IdParamError } from "@smonn/ids/express";
import { createTimestampId } from "@smonn/ids";
const usr = createTimestampId("usr");
// Default: forwards error to app error-handling middleware
app.get("/users/:id", idParam("id", usr), (req, res) => {
const id = res.locals.id; // Id<"usr">, canonical
});
// Error-handling middleware receives the typed error
app.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 response
app.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 handler
app.get("/things/:id", idParam("id", thing, { status: { brand_mismatch: 400 } }), handler);

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.

ParamKey extends string

Brand extends string

ParamKey

IdCodec<Brand>

IdParamOptions

(req, res, next) => void

import { idParam, IdParamError } from "@smonn/ids/express";
import { createTimestampId } from "@smonn/ids";
const usr = createTimestampId("usr");
// Default: forwards error to app error-handling middleware
app.get("/users/:id", idParam("id", usr), (req, res) => {
const id = res.locals.id; // Id<"usr">, canonical
});
// Error-handling middleware receives the typed error
app.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 response
app.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 handler
app.get("/things/:id", idParam("id", thing, { status: { brand_mismatch: 400 } }), handler);