Hooks
Control request and response lifecycle with Thunder's powerful pre and post hook system.
Lifecycle Overview
Hooks are one of Thunder's most powerful features. They give you precise, declarative control over the request/response lifecycle - without tangling middleware logic into your business code.
Every hook in Thunder is composed of three concerns:
- Priority - determines the execution order relative to other hooks.
preblock - runs before the route handler executes.postblock - runs after the route handler produces a response.

Hook Priority
Priority controls the order in which hooks execute relative to one another. Thunder supports any numeric priority value, as well as the special constants Infinity and -Infinity.
| Priority Value | Execution Order |
|---|---|
Infinity | First - runs before all other hooks |
1, 2, 3 ... | Ascending order - lower numbers run first |
-Infinity | Last - runs after all other hooks have completed |
Use Infinity for hooks that must always run first, such as security or authentication checks. Use -Infinity for cleanup or final logging hooks that should run after everything else.
Pre Hooks
A pre hook executes before the route handler is invoked. This is the ideal place for:
- Authentication and authorization checks
- Request validation
- Header injection or modification
- Rate limiting enforcement
export const authHook = {
priority: Infinity,
pre: async (req: Request) => {
const token = req.headers.get("Authorization");
if (!token) {
return Response.json({ error: "Unauthorized" }, { status: 401 });
}
// Token is valid - continue to handler
},
};Post Hooks
A post hook executes after the route handler has returned a response. This is the ideal place for:
- Request/response logging
- Analytics and telemetry
- Executing side effects (e.g. sending emails, invalidating cache)
export const loggingHook = {
priority: -Infinity,
post: async (req: Request, res: Response) => {
Logger.info(`[${req.method}] ${req.url} → ${res.status}`);
},
};Using Both Pre and Post
A single hook definition can include both a pre and a post block:
export const timingHook = {
priority: 1,
pre: async (req: Request) => {
// Attach a start timestamp to the request context
(req as any)._startTime = Date.now();
},
post: async (req: Request, res: Response) => {
const duration = Date.now() - (req as any)._startTime;
Logger.info(`Request completed in ${duration}ms`);
},
};Priority Examples
// Runs before everything else
export const securityHook = {
priority: Infinity,
pre: async (req: Request) => {
// Block suspicious requests
},
};// Hook A runs before Hook B
export const hookA = { priority: 1, pre: async () => { /* ... */ } };
export const hookB = { priority: 2, pre: async () => { /* ... */ } };// Runs after all other hooks complete
export const cleanupHook = {
priority: -Infinity,
post: async (req: Request, res: Response) => {
// Final cleanup or audit logging
},
};Hooks integrate seamlessly with Thunder's validation system. If a pre hook returns a Response, the route handler is skipped entirely and that response is sent directly to the client - making hooks an elegant mechanism for short-circuiting invalid requests.