Astro Actions
Guidelines for defining and using Astro Actions for server communication.
astro
## Astro Actions Guidelines
1. Purpose: Actions provide a type-safe way to define and call server-side functions (backend logic) from client-side code or HTML forms, simplifying data validation, fetching, and error handling compared to manual API endpoints for specific tasks like form submissions or mutations.
2. Definition (`src/actions/index.ts`):
- Create `src/actions/index.ts`.
- Export a `server` object containing your actions.
- Define each action using `defineAction` imported from `astro:actions`.
```typescript
// src/actions/index.ts
import { defineAction, z } from "astro:actions"; // z also available from 'astro:schema'
export const server = {
// Example action
likePost: defineAction({
// Optional: Define input schema using Zod for automatic validation
input: z.object({
postId: z.string(),
}),
// Handler function runs on the server
handler: async (input, context) => {
// input is validated based on the schema
// context provides APIContext (locals, cookies, request, etc.)
console.log(`Liking post ${input.postId}`);
// Check authorization using context.locals, context.cookies etc.
if (!context.locals.user) {
throw new ActionError({
code: "UNAUTHORIZED",
message: "Must be logged in.",
});
}
try {
// ... perform database update or other server logic ...
return { success: true, postId: input.postId };
} catch (e) {
console.error(e);
throw new ActionError({
code: "INTERNAL_SERVER_ERROR",
message: "Failed to like post.",
});
}
},
}),
// Add other actions...
};
```
3. Calling Actions (Client-Side):
- Import the `actions` object from `astro:actions`.
- Call actions as async functions: `const result = await actions.actionName(input);`.
- Works within `<script>` tags in `.astro` files or inside UI framework components.
```javascript
import { actions } from "astro:actions";
async function handleLike(postId) {
const { data, error } = await actions.likePost({ postId });
if (error) {
console.error("Action failed:", error.message, "Code:", error.code);
// Handle specific error codes like error.code === 'UNAUTHORIZED'
} else {
console.log("Action succeeded:", data);
}
}
```
4. Return Value (`{ data, error }`):
- Actions _always_ return an object with either a `data` property (on success) or an `error` property (on failure).
- `data`: The JSON-serializable value returned by the `handler`.
- `error`: An `ActionError` instance containing:
- `code`: A standardized error code string (e.g., `VALIDATION_ERROR`, `UNAUTHORIZED`, `INTERNAL_SERVER_ERROR`, `NOT_FOUND`).
- `message`: A description of the error.
- `fields` (for `VALIDATION_ERROR`): An object detailing validation errors per input field.
5. Input Validation:
- Defining an `input` schema with Zod in `defineAction` enables automatic validation.
- Works for both JSON payloads (from client-side calls) and `FormData` (from HTML forms).
- If validation fails, the action call returns an `error` with `code: 'VALIDATION_ERROR'` and details in `error.fields`.
6. HTML Form Integration:
- Use an action directly in a `<form>`'s `action` attribute. Import the specific action and use `action={actions.actionName}`.
- Method must be `POST`. Astro handles sending `FormData`.
- Access the result on the _next_ page load (after submission/redirect) using `Astro.getActionResult(actions.actionName)` in the page's frontmatter script. This returns the same `{ data, error }` structure or `undefined` if the action wasn't the last one run for that page load.
- Use this result to display success/error messages or validation feedback (`result.error?.fields`).
- For more complex state persistence across redirects (avoiding browser resubmission warnings), use middleware with `getActionContext` and session storage (see advanced docs).
7. Security:
- Actions create public endpoints under `/_actions/actionName`.
- Crucially, implement authorization checks within each action's `handler` using `context.locals`, cookies, etc. Throw an `ActionError({ code: 'UNAUTHORIZED' })` if the user is not permitted.
- Optionally, gate access to actions more broadly using middleware and `getActionContext` from `astro:actions`, but per-action checks in the handler are still recommended for fine-grained control.
8. Server-Side Calling:
- Call actions from `.astro` frontmatter using `Astro.callAction(actions.actionName, input)`.
- Call actions from endpoints or middleware using `context.callAction(actions.actionName, input)`.
- Returns the same `{ data, error }` structure. Useful for reusing action logic on the server.
Reference: [Astro Actions Docs](mdc:https:/docs.astro.build/en/guides/actions)