safe-action-middleware

Use when implementing middleware for next-safe-action -- authentication, authorization, logging, rate limiting, error interception, context extension, or creating standalone reusable middleware with createMiddleware()

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "safe-action-middleware" with this command: npx skills add next-safe-action/skills/next-safe-action-skills-safe-action-middleware

next-safe-action Middleware

Quick Start

import { createSafeActionClient } from "next-safe-action";

const actionClient = createSafeActionClient();

// Add middleware with .use()
const authClient = actionClient.use(async ({ next }) => {
  const session = await getSession();
  if (!session?.user) {
    throw new Error("Unauthorized");
  }
  // Pass context to the next middleware/action via next({ ctx })
  return next({
    ctx: { userId: session.user.id },
  });
});

How Middleware Works

  • .use() adds middleware to the chain — you can call it multiple times
  • Each .use() returns a new client instance (immutable chain)
  • Middleware executes top-to-bottom (in the order added)
  • Results flow bottom-to-top (the deepest middleware/action resolves first)
  • Context is accumulated via next({ ctx }) — each level's ctx is deep-merged with the previous
const client = createSafeActionClient()
  .use(async ({ next }) => {
    console.log("1: before");             // Runs 1st
    const result = await next({ ctx: { a: 1 } });
    console.log("1: after");              // Runs 4th
    return result;
  })
  .use(async ({ next, ctx }) => {
    console.log("2: before", ctx.a);      // Runs 2nd, ctx.a = 1
    const result = await next({ ctx: { b: 2 } });
    console.log("2: after");              // Runs 3rd
    return result;
  });

// In .action(): ctx = { a: 1, b: 2 }

Middleware Function Signature

async ({
  clientInput,           // Raw input from the client (unknown)
  bindArgsClientInputs,  // Raw bind args array
  ctx,                   // Accumulated context from previous middleware
  metadata,              // Metadata set via .metadata()
  next,                  // Call to proceed to next middleware/action
}) => {
  // Optionally extend context
  return next({ ctx: { /* new context properties */ } });
}

Supporting Docs

Anti-Patterns

// BAD: Forgetting to return next() — action will hang
.use(async ({ next }) => {
  await doSomething();
  next({ ctx: {} }); // Missing return!
})

// GOOD: Always return the result of next()
.use(async ({ next }) => {
  await doSomething();
  return next({ ctx: {} });
})
// BAD: Catching all errors (swallows framework errors like redirect/notFound)
.use(async ({ next }) => {
  try {
    return await next({ ctx: {} });
  } catch (error) {
    return { serverError: "Something went wrong" }; // Swallows redirect!
  }
})

// GOOD: Re-throw framework errors
.use(async ({ next }) => {
  try {
    return await next({ ctx: {} });
  } catch (error) {
    if (error instanceof Error && "digest" in error) {
      throw error; // Let Next.js handle redirects, notFound, etc.
    }
    // Handle other errors
    console.error(error);
    return { serverError: "Something went wrong" };
  }
})

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

safe-action-validation-errors

No summary provided by upstream source.

Repository SourceNeeds Review
General

safe-action-testing

No summary provided by upstream source.

Repository SourceNeeds Review
General

safe-action-forms

No summary provided by upstream source.

Repository SourceNeeds Review