idParam
Call Signature
Section titled “Call Signature”idParam<
ParamKey,Brand>(paramName,codec,options?): (request,reply) =>Promise<void>
Defined in: src/adapters/fastify.ts:147
Fastify preHandler hook factory that validates a named route param against a codec via safeParse.
Default (no options): throws IdParamError carrying statusCode and reason so the app’s
existing setErrorHandler controls rendering. The adapter does not write a response body itself.
options.onError: when provided, the adapter awaits the hook on validation failure. If the
hook sends a response (reply.sent is true after it resolves), the adapter takes no further
action. Otherwise, the adapter falls back to throwing 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 request.params[paramName] by mutating
the params object in place. This contrasts with the Express adapter, which writes to res.locals[paramName].
Fastify writes to request.params in place because Fastify’s preHandler lifecycle runs before the route
handler and its request object is the idiomatic side-channel for enriched request data; there is no
reply.locals equivalent in Fastify.
Return type note: the returned hook is typed as
(request: FastifyRequest<{ Params: Record<string, Id<Brand>> }>, reply: FastifyReply) => Promise<void>.
Assigning it to a Fastify preHandler slot is backward-compatible (method-signature bivariance applies).
However, a locally-annotated variable typed as the bare (request: FastifyRequest, reply: FastifyReply) => Promise<void>
will produce a TypeScript error under --strictFunctionTypes because function parameter types are contravariant.
Use preHandler assignment or let TypeScript infer the type to avoid this.
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”(request, reply) => Promise<void>
Example
Section titled “Example”import { idParam, IdParamError } from "@smonn/ids/fastify";import { createTimestampId } from "@smonn/ids";
const usr = createTimestampId("usr");
// Default: throws IdParamError → setErrorHandler renders itfastify.get("/users/:id", { preHandler: idParam("id", usr) }, (request, reply) => { const id = request.params.id; // string (compile-time); Id<"usr"> at runtime after preHandler});
// Error handler receives the typed errorfastify.setErrorHandler((err, request, reply) => { if (err instanceof IdParamError) { reply.status(err.statusCode).send({ error: err.reason }); return; } reply.send(err);});
// Override: consumer fully owns the error responsefastify.get("/orgs/:id", { preHandler: idParam("id", org, { onError: (failure, request, reply) => reply.status(failure.status).send({ error: failure.reason }), }),}, handler);
// Or a lightweight status remap without a full handlerfastify.get("/things/:id", { preHandler: idParam("id", thing, { status: { brand_mismatch: 400 } }),}, handler);Call Signature
Section titled “Call Signature”idParam<
ParamKey,Brand>(paramName,codec,options?): (request,reply) =>Promise<void>
Defined in: src/adapters/fastify.ts:155
Fastify preHandler hook factory that validates a named route param against a codec via safeParse.
Default (no options): throws IdParamError carrying statusCode and reason so the app’s
existing setErrorHandler controls rendering. The adapter does not write a response body itself.
options.onError: when provided, the adapter awaits the hook on validation failure. If the
hook sends a response (reply.sent is true after it resolves), the adapter takes no further
action. Otherwise, the adapter falls back to throwing 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 request.params[paramName] by mutating
the params object in place. This contrasts with the Express adapter, which writes to res.locals[paramName].
Fastify writes to request.params in place because Fastify’s preHandler lifecycle runs before the route
handler and its request object is the idiomatic side-channel for enriched request data; there is no
reply.locals equivalent in Fastify.
Return type note: the returned hook is typed as
(request: FastifyRequest<{ Params: Record<string, Id<Brand>> }>, reply: FastifyReply) => Promise<void>.
Assigning it to a Fastify preHandler slot is backward-compatible (method-signature bivariance applies).
However, a locally-annotated variable typed as the bare (request: FastifyRequest, reply: FastifyReply) => Promise<void>
will produce a TypeScript error under --strictFunctionTypes because function parameter types are contravariant.
Use preHandler assignment or let TypeScript infer the type to avoid this.
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”(request, reply) => Promise<void>
Example
Section titled “Example”import { idParam, IdParamError } from "@smonn/ids/fastify";import { createTimestampId } from "@smonn/ids";
const usr = createTimestampId("usr");
// Default: throws IdParamError → setErrorHandler renders itfastify.get("/users/:id", { preHandler: idParam("id", usr) }, (request, reply) => { const id = request.params.id; // string (compile-time); Id<"usr"> at runtime after preHandler});
// Error handler receives the typed errorfastify.setErrorHandler((err, request, reply) => { if (err instanceof IdParamError) { reply.status(err.statusCode).send({ error: err.reason }); return; } reply.send(err);});
// Override: consumer fully owns the error responsefastify.get("/orgs/:id", { preHandler: idParam("id", org, { onError: (failure, request, reply) => reply.status(failure.status).send({ error: failure.reason }), }),}, handler);
// Or a lightweight status remap without a full handlerfastify.get("/things/:id", { preHandler: idParam("id", thing, { status: { brand_mismatch: 400 } }),}, handler);