Action Middlewares
You can register a action middleware function by calling use
method on your action router
instance and providing a function that return promise of context.
Technically, All middleware types have same function signature and they all serves the same purpose. You can categorize a middleware based on where you are registering it in the chain/action routing tree.
Execution Order: Middlewares get executed in the order in which they are chained together.
📝 NOTE: For example project structure reference, click here
Middlewares can be categorized in three different levels:
- Global level middlewares
- Sub-Router level middlewares
- Server action level middlewares
Let's explore each of them one by one!
Middleware function
As I mentioned it above, there's no technical difference between the middleware categories. Regardless, what you call it under the hood they have same signature, purpose and constraints.
Every middleware will receieve an object of type ActionRequest
as an argument.
Type Definition:
import { cookies, headers } from "next/headers";
type ActionRequest<TContext> = {
context: TContext;
headers: ReturnType<typeof headers>;
cookies: ReturnType<typeof cookies>;
};
type ActionMiddleware<TContext, TReturn> = (
request: ActionRequest<TContext>
) => Promise<TReturn>;
Base context: { inputs: void }
(Initial value)
Context: It's just a plain old javaScript object (aka POJO ). Intially having only "inputs" as a key. We'll talk about this in detail in upcoming pages.
⚠️ important: For now the only thing that you need to know is that always return a pojo from your middleware function. It's recommended that you just either return the same context as it is or extend it by adding few more properties on it before returning it. The returned value from a middleware will serve as new context for the next middleware in chain.
// --snip--
.use(async(request) => {
console.log(request.context);
console.log(request.cookies);
console.log(request.headers);
// if you haven't extended the original context
// then just simply return the original one
return request.context;
});
Middleware levels
In short, The middlewares registered at the very root of the router instance are called as global Middlewares and they will run for every single action request hitting the same router instance.
UseCase: Request logging, rate limiting, auth check, etc
// lib/action.router.ts
import { ActionRouter } from "next-action-router/server";
export const rootRouter = new ActionRouter({
// router config
})
.use(async ({ context }) => {
console.log("Global middleware 1");
return context;
})
.use(async ({ context }) => {
console.log("Global middleware 2");
return context;
})
.use(async ({ context }) => {
console.log("Global middleware 3");
return context;
});
Middleware execution order:
Global middleware 1
Global middleware 2
Global middleware 3