{"id":49699118,"url":"https://github.com/programming-with-ia/better-next-actions","last_synced_at":"2026-05-08T06:03:39.983Z","repository":{"id":324095248,"uuid":"1093405657","full_name":"programming-with-ia/better-next-actions","owner":"programming-with-ia","description":"A typesafe and structured way to build Next.js Server Actions with middleware and validation.","archived":false,"fork":false,"pushed_at":"2025-11-13T18:51:00.000Z","size":32,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-11-13T20:25:51.820Z","etag":null,"topics":["actions-lib","better-actions","better-next-actions","middlewares","next-safe-actions","next-server-action","nextjs","react-query","safe-actions","server-actions","trpc","typesafe","use-action","zod"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/programming-with-ia.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-10T10:27:17.000Z","updated_at":"2025-11-13T18:51:04.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/programming-with-ia/better-next-actions","commit_stats":null,"previous_names":["programming-with-ia/better-next-actions"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/programming-with-ia/better-next-actions","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/programming-with-ia%2Fbetter-next-actions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/programming-with-ia%2Fbetter-next-actions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/programming-with-ia%2Fbetter-next-actions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/programming-with-ia%2Fbetter-next-actions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/programming-with-ia","download_url":"https://codeload.github.com/programming-with-ia/better-next-actions/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/programming-with-ia%2Fbetter-next-actions/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32769110,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-08T02:36:36.067Z","status":"ssl_error","status_checked_at":"2026-05-08T02:36:07.210Z","response_time":54,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["actions-lib","better-actions","better-next-actions","middlewares","next-safe-actions","next-server-action","nextjs","react-query","safe-actions","server-actions","trpc","typesafe","use-action","zod"],"created_at":"2026-05-08T06:03:37.945Z","updated_at":"2026-05-08T06:03:39.973Z","avatar_url":"https://github.com/programming-with-ia.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Next.js Server Actions Client\n\nA 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.\n\n## Core Concepts\n\nThis library empowers you to build robust Next.js Server Actions by providing:\n\n- **End-to-end Type Safety:** Automatically infer types from your Zod schemas and middleware, ensuring your actions are typesafe from client to server.\n- **Reusable Middleware:** Define and compose middleware to handle common tasks like authentication, authorization, and logging.\n- **Zod Schema Validation:** Validate action payloads with Zod schemas, providing clear and concise error messages.\n- **Centralized Action Clients:** Create different action clients (e.g., for public, protected, or admin-only actions) in a single, organized file.\n\n## Installation\n\n```bash\nnpm install better-next-actions\n```\n\n## Setup: The Action Client\n\nIt's recommended to create a single file to define all your action clients and middleware. This keeps your code organized and easy to maintain.\n\nCreate a file at `/lib/action-client.ts`:\n\n```typescript\n// /lib/action-client.ts\nimport \"server-only\";\nimport { createActionClient, ActionError } from \"better-next-actions\";\n\n// This is your base, unauthenticated action client.\nexport const publicActionClient = createActionClient();\n\n// --- Example: Middleware for authentication ---\nconst authMiddleware = async () =\u003e {\n  // In a real app, you'd get the user session here.\n  const user = { id: \"user_123\" }; // Mock user\n  if (!user) {\n    throw new ActionError({ code: \"UNAUTHORIZED\", message: \"Not logged in.\" });\n  }\n  return { user };\n};\n\n// Create a new client that uses the auth middleware.\nexport const protectedActionClient = publicActionClient.use(authMiddleware);\n// export const protectedActionClient = createActionClient().use(authMiddleware); // or use new client\n\n// --- Example: Middleware for admin checks ---\nconst adminMiddleware = async (ctx: { user: { id: string } }) =\u003e {\n  // This middleware runs *after* authMiddleware, so `ctx.user` is available.\n  if (ctx.user.id !== \"user_123\") { // Mock admin check\n      throw new ActionError({ code: \"FORBIDDEN\", message: \"You are not an admin.\" });\n  }\n  return { isAdmin: true };\n}\n\n// Create a new client that stacks both middlewares.\nexport const adminActionClient = protectedActionClient.use(adminMiddleware);\n```\n\nNow you can import these clients into your server actions.\n\n## Usage\n\n### Using Middleware\n\nCreate your actions by importing your clients and defining the action handler.\n\n```typescript\n// app/actions.ts\n\"use server\";\n\nimport { z } from \"zod\";\nimport { protectedActionClient, adminActionClient } from \"@/lib/action-client\";\n\n// --- Protected Action ---\nexport const getMyProfile = protectedActionClient.action(\n  async (payload, ctx) =\u003e {\n    // `ctx` is typesafe: { user: { id: string } }\n    console.log(\"Fetching profile for user:\", ctx.user.id);\n    return { id: ctx.user.id, name: \"Test User\" };\n  }\n);\n\n// --- Admin Action with Zod Validation ---\nconst updateSystemSettingsSchema = z.object({\n  maintenanceMode: z.boolean(),\n});\n\nexport const setMaintenanceMode = adminActionClient\n  .input(updateSystemSettingsSchema)\n  .action(async (data, ctx) =\u003e {\n    // `data` is typesafe: { maintenanceMode: boolean }\n    // `ctx` is typesafe: { user: { id: string }, isAdmin: true }\n    console.log(\n      `Admin ${ctx.user.id} is setting maintenance mode to ${data.maintenanceMode}`\n    );\n    return { success: true, ...data };\n  });\n```\n\n### Reusable Schemas\n\nYou can create a client with a pre-defined schema that can be reused across multiple actions.\n\n```typescript\n// /lib/action-client.ts\n\n// ... (previous code)\nimport { z } from \"zod\";\n\nexport const withIdClient = publicActionClient.input(z.object({ id: z.string().length(6) }));\n\n// app/actions.ts\nimport { withIdClient } from \"@/lib/action-client\";\n\nexport const getPostById = withIdClient.action(async (data, ctx) =\u003e {\n  // `data` is typesafe: { id: string }\n  console.log(\"Fetching post with ID:\", data.id);\n  return { id: data.id, title: \"Post Title\" };\n});\n\nexport const deletePostById = withIdClient.action(async (data, ctx) =\u003e {\n  // `data` is typesafe: { id: string }\n  console.log(\"Deleting post with ID:\", data.id);\n  return { success: true, deletedId: data.id };\n});\n```\n\n### Error Handling\n\nWhen an action fails, it returns an `error` object. You can check for this object on the client to handle errors gracefully.\n\n```typescript\n// app/page.tsx\n\"use client\";\n\nimport { setMaintenanceMode } from \"./actions\";\n\nexport default function HomePage() {\n  const handleAction = async () =\u003e {\n    const result = await setMaintenanceMode({ maintenanceMode: true });\n    if (result.error) {\n      alert(`Error: ${result.error.message}`);\n      return;\n    }\n    // Handle success\n    console.log(result.data);\n  };\n\n  return \u003cbutton onClick={handleAction}\u003eSet Maintenance Mode\u003c/button\u003e;\n}\n```\n\n## Optional: `react-query` Hooks\n\nThis 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.\n\n**To add the hooks:**\n\n1. Create a new directory in your project, for example, `/lib/hooks`.\n2. Copy the contents of `evoo/use-action-mutation.ts` and `evoo/use-query-action.ts` into this new directory.\n3. You can now import and use these hooks in your client components.\n\n**Note:** You will need to have `react-query` installed in your project to use these hooks.\n\n## Companion for API Routes: `better-next-api`\n\nWhile `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`](https://github.com/programming-with-ia/better-next-api).\n\nIt offers a typesafe and structured way to build your API routes with features like:\n\n- **App Router Ready:** Built specifically for the Next.js App Router.\n- **Middleware Support:** Compose and reuse middleware for your API routes.\n- **Zod Schema Validation:** End-to-end validation for `searchParams` (GET), `params`, and the request `body` (POST, PUT, etc.).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprogramming-with-ia%2Fbetter-next-actions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprogramming-with-ia%2Fbetter-next-actions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprogramming-with-ia%2Fbetter-next-actions/lists"}