API Reference
createMiddleware()
createMiddleware creates a standalone middleware function that can be shared across multiple action clients. It provides type constraints so TypeScript can verify the middleware is only used with compatible clients.
import { createMiddleware } from "next-safe-action";
const myMiddleware = createMiddleware<Constraints>().define(middlewareFn);Signature
function createMiddleware<
BaseData extends {
serverError?: any;
ctx?: object;
metadata?: any;
}
>(): {
define: <NextCtx extends object>(middlewareFn: MiddlewareFn) => MiddlewareFn;
};Generic parameter
createMiddleware accepts a single generic parameter BaseData that declares the minimum requirements for the middleware. The client using this middleware must have at least these properties available.
Prop
Type
.define() method
The define method accepts a middleware function and returns it with proper typing. The middleware function receives the same options as inline .use() middleware:
Prop
Type
Examples
No constraints (universal middleware)
import { createMiddleware } from "next-safe-action";
export const loggingMiddleware = createMiddleware().define(
async ({ next, metadata }) => {
const start = Date.now();
const result = await next();
console.log(`Action took ${Date.now() - start}ms`, metadata);
return result;
}
);With context constraints
import { createMiddleware } from "next-safe-action";
// Requires ctx.user with a role property
export const adminGuard = createMiddleware<{
ctx: { user: { id: string; role: string } };
}>().define(async ({ next, ctx }) => {
if (ctx.user.role !== "admin") {
throw new Error("Admin access required");
}
// ctx.user is fully typed here
return next();
});With metadata constraints
import { createMiddleware } from "next-safe-action";
export const rateLimitMiddleware = createMiddleware<{
metadata: { actionName: string };
}>().define(async ({ next, metadata }) => {
await checkRateLimit(metadata.actionName);
return next();
});Extending context
import { createMiddleware } from "next-safe-action";
export const withDb = createMiddleware().define(async ({ next }) => {
const db = await getDbConnection();
return next({
ctx: { db }, // Adds db to context for downstream middleware/actions
});
});Usage with clients
import { createSafeActionClient } from "next-safe-action";
import { loggingMiddleware } from "./middleware/logging";
import { adminGuard } from "./middleware/admin-guard";
const baseClient = createSafeActionClient().use(loggingMiddleware);
// ✅ Works — adminGuard requires ctx.user, which authMiddleware provides
const adminClient = baseClient
.use(async ({ next }) => {
const user = await getUser();
return next({ ctx: { user } });
})
.use(adminGuard);
// ❌ Type error — adminGuard requires ctx.user, but baseClient doesn't have it
const broken = baseClient.use(adminGuard);See also
- Middleware guide — foundational middleware concepts
- Standalone Middleware — guide with more patterns and use cases
.use()method — how standalone middleware is consumed by clients