next-safe-action
API reference

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

src/lib/middleware/log-user.ts
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

src/lib/middleware/ownership-check.ts
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

src/lib/safe-action.ts
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 schema

See also

On this page