# Guide: Creating Server Actions

This guide explains how to create and use standardized Server Actions in the Jomblo project.

## 🚀 The `createServerAction` Wrapper

All server actions must be created using the `createServerAction` factory. This ensures:
1. **Zod Validation**: Automatic input validation and type inference.
2. **Security**: Role-based access control (RBAC) via `requireAuth` and `requireAdmin`.
3. **Standardized Responses**: Consistent `ActionResponse` structure.
4. **Traceability**: Automatic logging context (request IDs) restoration.
5. **Rate Limiting**: Integrated throttling to prevent abuse.

---

## 🛠️ Step-by-Step Implementation

### 1. Define the Schema
Define your input schema in `schemas/api.ts` or locally in the action file.

```typescript
const MyActionSchema = z.object({
  id: z.number(),
  name: z.string().min(1),
});
```

### 2. Create the Action
Use the wrapper in your action file (e.g., `actions/my-module.ts`).

```typescript
"use server";

import { createServerAction } from "@/lib/actions";
import { MyRepository } from "@/lib/db/repositories/my.repository";

const repo = new MyRepository();

export const myAction = createServerAction({
  schema: MyActionSchema,
  requireAdmin: true, // Optional: restricts to admin users
  rateLimit: { limit: 5, windowSeconds: 60 }, // Optional: throttles requests
  handler: async ({ input, user }) => {
    // Business logic goes here
    const result = await repo.performOperation(input.id, input.name);
    return result; // Data will be wrapped in { success: true, data: ... }
  },
});
```

### 3. Use in the Frontend
Use the `useAction` hook in your React components.

```typescript
"use client";

import { useAction } from "@/hooks/useAction";
import { myAction } from "@/actions/my-module";

export function MyComponent() {
  const { execute, isLoading, data, validationErrors } = useAction(myAction);

  const onSubmit = async () => {
    const result = await execute({ id: 1, name: "New Name" });
    if (result?.success) {
      // Success is handled (toast shown automatically if result has a message)
    }
  };

  return (
    <button onClick={onSubmit} disabled={isLoading}>
      {isLoading ? "Saving..." : "Save"}
    </button>
  );
}
```

---

## ⚠️ Error Handling

Use specialized error classes from `@/lib/errors` within your handlers. The wrapper automatically catches these and converts them to standard error responses.

| Error Class | Status Code | Usage |
|-------------|-------------|-------|
| `NotFoundError` | 404 | Entity not found in DB |
| `BadRequestError` | 400 | Invalid business logic state |
| `UnauthorizedError` | 401 | Missing authentication |
| `ForbiddenError` | 403 | Insufficient permissions |
| `ConflictError` | 409 | Duplicate records / Unique constraints |

---

## 📝 Best Practices
- **Repositories**: Never use `db.query` or `pool.query` inside a server action. Always delegate to a repository.
- **CamelCase**: Server actions and DTOs should use `camelCase`. Map `snake_case` from the database in your repository or a Zod `preprocess`.
- **Validation**: Rely on the wrapper for validation. Do not manualy catch ZodErrors unless you need custom re-mapping.
- **Revalidation**: Call `revalidatePath` or `revalidateTag` inside the handler for mutations to update the Next.js cache.
