createValidatedMiddleware()
createValidatedMiddleware creates a standalone validated middleware function for use with .useValidated(). It works the same way as createMiddleware(), but the middleware function also receives typed parsedInput, clientInput, bindArgsParsedInputs, and bindArgsClientInputs.
import { createValidatedMiddleware } from "next-safe-action";
const myMiddleware = createValidatedMiddleware<Constraints>().define(middlewareFn);Most middleware should use createMiddleware() with .use() instead. Only use createValidatedMiddleware() when the middleware specifically needs access to the validated parsedInput, for example to check resource ownership or log transformed input. See when to use useValidated() vs use() for guidance.
Signature
function createValidatedMiddleware<
BaseData extends {
serverError?: any;
ctx?: object;
metadata?: any;
parsedInput?: unknown;
clientInput?: unknown;
bindArgsParsedInputs?: readonly unknown[];
bindArgsClientInputs?: readonly unknown[];
}
>(): {
define: <NextCtx extends object>(middlewareFn: ValidatedMiddlewareFn) => ValidatedMiddlewareFn;
};Generic parameter
createValidatedMiddleware accepts a single generic parameter BaseData with all the properties from createMiddleware, plus input-related constraints:
Prop
Type
.define() method
The define method accepts a validated middleware function. The function receives all the properties from inline useValidated() middleware: parsedInput, clientInput, bindArgsParsedInputs, bindArgsClientInputs, ctx, metadata, and next.
Examples
With parsedInput constraint
import { createValidatedMiddleware } from "next-safe-action";
export const logUserMiddleware = createValidatedMiddleware<{
parsedInput: { userId: string };
}>().define(async ({ parsedInput, next }) => {
console.log("Acting on user:", parsedInput.userId);
return next();
});With context and parsedInput constraints
import { createValidatedMiddleware } from "next-safe-action";
export const ownershipCheck = createValidatedMiddleware<{
ctx: { user: { id: string } };
parsedInput: { resourceId: string };
}>().define(async ({ parsedInput, ctx, next }) => {
const resource = await db.resource.findUnique({
where: { id: parsedInput.resourceId },
});
if (resource?.ownerId !== ctx.user.id) {
throw new Error("Forbidden");
}
return next({ ctx: { resource } });
});Usage with clients
import { ownershipCheck } from "./middleware/ownership-check";
const ownerAction = authClient
.inputSchema(z.object({ resourceId: z.string() }))
.useValidated(ownershipCheck);
// ✅ Works: ownershipCheck requires ctx.user (from auth) and parsedInput.resourceId
// ❌ Type error if used without auth middleware or without resourceId in schemaSee also
- Validated middleware guide: practical patterns for
useValidated() - Standalone middleware: guide with reuse patterns
.useValidated()method: how standalone validated middleware is consumedcreateMiddleware(): standalone pre-validation middleware factory