npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

better-next-actions-alpha.1

v1.0.0

Published

A lightweight, modern hook for calling Next.js server actions with confidence.

Readme

Next.js Server Actions Client

A typesafe and structured way to build Next.js Server Actions with middleware and validation. This library provides a tRPC-like experience for the Next.js Action paradigm.

Core Concepts

This library empowers you to build robust Next.js Server Actions by providing:

  • End-to-end Type Safety: Automatically infer types from your Zod schemas and middleware, ensuring your actions are typesafe from client to server.
  • Reusable Middleware: Define and compose middleware to handle common tasks like authentication, authorization, and logging.
  • Zod Schema Validation: Validate action payloads with Zod schemas, providing clear and concise error messages.
  • Centralized Action Clients: Create different action clients (e.g., for public, protected, or admin-only actions) in a single, organized file.

Installation

npm install better-next-actions

Setup: The Action Client

It's recommended to create a single file to define all your action clients and middleware. This keeps your code organized and easy to maintain.

Create a file at /lib/action-client.ts:

// /lib/action-client.ts
import "server-only";
import { createActionClient, ActionError } from "better-next-actions";

// This is your base, unauthenticated action client.
export const publicActionClient = createActionClient();

// --- Example: Middleware for authentication ---
const authMiddleware = async () => {
  // In a real app, you'd get the user session here.
  const user = { id: "user_123" }; // Mock user
  if (!user) {
    throw new ActionError({ code: "UNAUTHORIZED", message: "Not logged in." });
  }
  return { user };
};

// Create a new client that uses the auth middleware.
export const protectedActionClient = publicActionClient.use(authMiddleware);
// export const protectedActionClient = createActionClient().use(authMiddleware); // or use new client

// --- Example: Middleware for admin checks ---
const adminMiddleware = async (ctx: { user: { id: string } }) => {
  // This middleware runs *after* authMiddleware, so `ctx.user` is available.
  if (ctx.user.id !== "user_123") { // Mock admin check
      throw new ActionError({ code: "FORBIDDEN", message: "You are not an admin." });
  }
  return { isAdmin: true };
}

// Create a new client that stacks both middlewares.
export const adminActionClient = protectedActionClient.use(adminMiddleware);

Now you can import these clients into your server actions.

Usage

Using Middleware

Create your actions by importing your clients and defining the action handler.

// app/actions.ts
"use server";

import { z } from "zod";
import { protectedActionClient, adminActionClient } from "@/lib/action-client";

// --- Protected Action ---
export const getMyProfile = protectedActionClient.action(
  async (payload, ctx) => {
    // `ctx` is typesafe: { user: { id: string } }
    console.log("Fetching profile for user:", ctx.user.id);
    return { id: ctx.user.id, name: "Test User" };
  }
);

// --- Admin Action with Zod Validation ---
const updateSystemSettingsSchema = z.object({
  maintenanceMode: z.boolean(),
});

export const setMaintenanceMode = adminActionClient
  .input(updateSystemSettingsSchema)
  .action(async (data, ctx) => {
    // `data` is typesafe: { maintenanceMode: boolean }
    // `ctx` is typesafe: { user: { id: string }, isAdmin: true }
    console.log(
      `Admin ${ctx.user.id} is setting maintenance mode to ${data.maintenanceMode}`
    );
    return { success: true, ...data };
  });

Reusable Schemas

You can create a client with a pre-defined schema that can be reused across multiple actions.

// /lib/action-client.ts

// ... (previous code)
import { z } from "zod";

export const withIdClient = publicActionClient.input(z.object({ id: z.string().length(6) }));

// app/actions.ts
import { withIdClient } from "@/lib/action-client";

export const getPostById = withIdClient.action(async (data, ctx) => {
  // `data` is typesafe: { id: string }
  console.log("Fetching post with ID:", data.id);
  return { id: data.id, title: "Post Title" };
});

export const deletePostById = withIdClient.action(async (data, ctx) => {
  // `data` is typesafe: { id: string }
  console.log("Deleting post with ID:", data.id);
  return { success: true, deletedId: data.id };
});

Error Handling

When an action fails, it returns an error object. You can check for this object on the client to handle errors gracefully.

// app/page.tsx
"use client";

import { setMaintenanceMode } from "./actions";

export default function HomePage() {
  const handleAction = async () => {
    const result = await setMaintenanceMode({ maintenanceMode: true });
    if (result.error) {
      alert(`Error: ${result.error.message}`);
      return;
    }
    // Handle success
    console.log(result.data);
  };

  return <button onClick={handleAction}>Set Maintenance Mode</button>;
}

Optional: react-query Hooks

This package does not include react-query hooks by default to keep the core library dependency-free. If you wish to use react-query with your server actions, you can manually copy the hook files from the evoo/ directory into your project.

To add the hooks:

  1. Create a new directory in your project, for example, /lib/hooks.
  2. Copy the contents of evoo/use-action-mutation.ts and evoo/use-query-action.ts into this new directory.
  3. You can now import and use these hooks in your client components.

Note: You will need to have react-query installed in your project to use these hooks.

Companion for API Routes: better-next-api

While better-next-actions is designed for Next.js Server Actions, you might need a similar solution for traditional API routes in the App Router. For that, we recommend checking out better-next-api.

It offers a typesafe and structured way to build your API routes with features like:

  • App Router Ready: Built specifically for the Next.js App Router.
  • Middleware Support: Compose and reuse middleware for your API routes.
  • Zod Schema Validation: End-to-end validation for searchParams (GET), params, and the request body (POST, PUT, etc.).