Acciones
Añadido en:
astro@4.15
Las acciones de Astro te permiten definir y llamar a funciones del backend con seguridad de tipos. Las acciones realizan la obtención de datos, el análisis de JSON y la validación de entradas por ti. Esto puede reducir en gran medida la cantidad de código repetitivo (boilerplate) necesario en comparación con el uso de un endpoint de API.
Utiliza acciones en lugar de endpoints de API para una comunicación fluida entre el código de tu cliente y del servidor, y para:
- Valida automáticamente las entradas de datos JSON y de formulario mediante la validación de Zod.
- Genera funciones seguras en cuanto a tipos para llamar a tu backend desde el cliente e incluso desde acciones de formulario HTML. No es necesario realizar llamadas manuales a
fetch(). - Estandariza los errores del backend con el objeto
ActionError.
Uso básico
Sección titulada “Uso básico”Las acciones se definen en un objeto server exportado desde src/actions/index.ts:
import { defineAction } from 'astro:actions';import { z } from 'astro/zod';
export const server = { myAction: defineAction({ /* ... */ })}Tus acciones están disponibles como funciones desde el módulo astro:actions. Importa actions y llámalas en el lado del cliente dentro de un componente de framework de UI, en una solicitud POST de formulario o utilizando una etiqueta <script> en un componente de Astro.
Cuando llamas a una acción, esta devuelve un objeto con data que contiene el resultado serializado en JSON, o bien error que contiene los errores lanzados.
------
<script>import { actions } from 'astro:actions';
async () => { const { data, error } = await actions.myAction({ /* ... */ });}</script>Escribe tu primera acción
Sección titulada “Escribe tu primera acción”Sigue estos pasos para definir una acción y llamarla en una etiqueta script en tu página de Astro.
-
Crea un archivo
src/actions/index.tsy exporta un objetoserver.src/actions/index.ts export const server = {// action declarations} -
Importa la utilidad
defineAction()desdeastro:actionsy el objetozdesdeastro/zod.src/actions/index.ts import { defineAction } from 'astro:actions';import { z } from 'astro/zod';export const server = {// action declarations} -
Utiliza la utilidad
defineAction()para definir una accióngetGreeting. La propiedadinputse utilizará para validar los parámetros de entrada con un esquema de Zod, y la funciónhandler()incluye la lógica del backend que se ejecutará en el servidor.src/actions/index.ts import { defineAction } from 'astro:actions';import { z } from 'astro/zod';export const server = {getGreeting: defineAction({input: z.object({name: z.string(),}),handler: async (input) => {return `Hello, ${input.name}!`}})} -
Crea un componente de Astro con un botón que, al hacer clic, obtenga un saludo utilizando tu acción
getGreeting.src/pages/index.astro ------<button>Get greeting</button><script>const button = document.querySelector('button');button?.addEventListener('click', async () => {// Show alert pop-up with greeting from action});</script> -
Para usar tu acción, importa
actionsdesdeastro:actionsy luego llama aactions.getGreeting()en el controlador del clic. La opciónnamese enviará alhandler()de tu acción en el servidor y, si no hay errores, el resultado estará disponible como la propiedaddata.src/pages/index.astro ------<button>Get greeting</button><script>import { actions } from 'astro:actions';const button = document.querySelector('button');button?.addEventListener('click', async () => {// Show alert pop-up with greeting from actionconst { data, error } = await actions.getGreeting({ name: "Houston" });if (!error) alert(data);})</script>
defineAction() y sus propiedades.
Organización de acciones
Sección titulada “Organización de acciones”Todas las acciones de tu proyecto deben exportarse desde el objeto server en el archivo src/actions/index.ts. Puedes definir acciones en línea o puedes mover las definiciones de las acciones a archivos separados e importarlas. Incluso puedes agrupar funciones relacionadas en objetos anidados.
Por ejemplo, para colocar juntas todas las acciones de tus usuarios, puedes crear un archivo src/actions/user.ts y anidar las definiciones tanto de getUser como de createUser dentro de un único objeto user.
import { defineAction } from 'astro:actions';
export const user = { getUser: defineAction(/* ... */), createUser: defineAction(/* ... */),}Luego, puedes importar este objeto user en tu archivo src/actions/index.ts y agregarlo como una clave de nivel superior al objeto server junto con cualquier otra acción:
import { user } from './user';
export const server = { myAction: defineAction({ /* ... */ }), user,}Ahora, todas las acciones de tus usuarios se pueden llamar desde el objeto actions.user:
actions.user.getUser()actions.user.createUser()
Manejo de datos devueltos
Sección titulada “Manejo de datos devueltos”Las acciones devuelven un objeto que contiene data con el valor de retorno seguro en cuanto a tipos de tu handler(), o bien un error con cualquier error del backend. Los errores pueden provenir de errores de validación en la propiedad input o de errores lanzados dentro del handler().
Las acciones devuelven un formato de datos personalizado que puede manejar fechas (Dates), mapas (Maps), conjuntos (Sets) y URLs utilizando la biblioteca Devalue. Por lo tanto, no puedes inspeccionar fácilmente la respuesta desde la red como lo harías con un JSON normal. Para la depuración, puedes inspeccionar en su lugar el objeto data devuelto por las acciones.
handler() para obtener todos los detalles.
Comprobación de errores
Sección titulada “Comprobación de errores”Es mejor verificar si hay un error presente antes de usar la propiedad data. Esto te permite manejar errores con anticipación y garantiza que data esté definido sin una comprobación de undefined.
const { data, error } = await actions.example();
if (error) { // handle error cases return;}// use `data`Acceder a data directamente sin una comprobación de errores
Sección titulada “Acceder a datos directamente sin una comprobación de errores”Para omitir el manejo de errores, por ejemplo durante la creación de prototipos o al usar una biblioteca que capture errores por ti, usa la propiedad .orThrow() en tu llamada a la acción para lanzar errores en lugar de devolver un error. Esto devolverá directamente el valor data de la acción.
Este ejemplo llama a una acción likePost() que devuelve el número actualizado de "me gusta" como un number desde el handler de la acción:
const updatedLikes = await actions.likePost.orThrow({ postId: 'example' });// ^ type: numberManejo de errores del backend en tu acción
Sección titulada “Manejo de errores del backend en tu acción”Puedes usar el objeto ActionError proporcionado para lanzar un error desde el handler() de tu acción, como "not found" (no encontrado) cuando falta una entrada en la base de datos, o "unauthorized" (no autorizado) cuando un usuario no ha iniciado sesión. Esto tiene dos beneficios principales sobre devolver undefined:
-
Puedes establecer un código de estado como
404 - Not foundo401 - Unauthorized. Esto mejora la depuración de errores tanto en desarrollo como en producción al permitirte ver el código de estado de cada solicitud. -
En el código de tu aplicación, todos los errores se pasan al objeto
erroren el resultado de una acción. Esto evita la necesidad de realizar comprobaciones deundefineden los datos y te permite mostrar comentarios específicos al usuario según lo que haya salido mal.
Crear un ActionError
Sección titulada “Crear un ActionError”Para lanzar un error, importa la clase la clase ActionError() desde el módulo astro:actions. Pásale un código code de estado legible por humanos (p. ej. "NOT_FOUND" o "BAD_REQUEST") y un mensaje message opcional para proporcionar más información sobre el error.
Este ejemplo lanza un error desde una acción likePost cuando un usuario no ha iniciado sesión, después de verificar una hipotética cookie "user-session" para la autenticación:
import { defineAction, ActionError } from "astro:actions";import { z } from "astro/zod";
export const server = { likePost: defineAction({ input: z.object({ postId: z.string() }), handler: async (input, ctx) => { if (!ctx.cookies.has('user-session')) { throw new ActionError({ code: "UNAUTHORIZED", message: "User must be logged in.", }); } // Otherwise, like the post }, }),};Manejar un ActionError
Sección titulada “Manejar un ActionError”Para manejar este error, puedes llamar a la acción desde tu aplicación y verificar si la propiedad error está presente. Esta propiedad será de tipo ActionError y contendrá tu código code y tu mensaje message.
En el siguiente ejemplo, un componente LikeButton.tsx llama a la acción likePost() al hacer clic. Si ocurre un error de autenticación, el atributo error.code se utiliza para determinar si se debe mostrar un enlace de inicio de sesión:
import { actions } from 'astro:actions';import { useState } from 'preact/hooks';
export function LikeButton({ postId }: { postId: string }) { const [showLogin, setShowLogin] = useState(false); return ( <> { showLogin && <a href="/signin">Log in to like a post.</a> } <button onClick={async () => { const { data, error } = await actions.likePost({ postId }); if (error?.code === 'UNAUTHORIZED') setShowLogin(true); // Early return for unexpected errors else if (error) return; // update likes }}> Like </button> </> )}Manejo de redirecciones del cliente
Sección titulada “Manejo de redirecciones del cliente”Al llamar a acciones desde el cliente, puedes integrarlas con una biblioteca del lado del cliente como react-router, o puedes usar la función la función navigate() de Astro para redirigir a una nueva página cuando una acción tenga éxito.
Este ejemplo navega a la página de inicio después de que una acción de logout se completa con éxito:
import { actions } from 'astro:actions';import { navigate } from 'astro:transitions/client';
export function LogoutButton() { return ( <button onClick={async () => { const { error } = await actions.logout(); if (!error) navigate('/'); }}> Logout </button> );}Aceptar datos de formulario de una acción
Sección titulada “Aceptar datos de formulario de una acción”Las acciones aceptan datos JSON por defecto. Para aceptar datos de formulario de un formulario HTML, establece accept: 'form' en tu llamada a defineAction():
import { defineAction } from 'astro:actions';import { z } from 'astro/zod';
export const server = { comment: defineAction({ accept: 'form', input: z.object(/* ... */), handler: async (input) => { /* ... */ }, })}Uso de validadores con entradas de formulario
Sección titulada “Uso de validadores con entradas de formulario”Cuando tu acción está configurada para aceptar datos de formulario, puedes usar cualquier validador de Zod para validar tus campos (p. ej., z.coerce.date() para entradas de fecha). Las funciones de extensión, incluidas .refine(), .transform() y .pipe(), también son compatibles con el validador z.object().
Además, Astro proporciona un manejo especial bajo el capó para tu conveniencia al validar los siguientes tipos de entradas de campo:
- Las entradas de tipo
numberse pueden validar usandoz.number() - Las entradas de tipo
checkboxse pueden validar usandoz.coerce.boolean() - Las entradas de tipo
filese pueden validar usandoz.instanceof(File) - Las entradas múltiples con el mismo
namese pueden validar usandoz.array(/* validador */) - Todas las demás entradas se pueden validar usando
z.string()
Cuando tu formulario se envía con entradas vacías, es posible que el tipo de salida no coincida con tu validador input. Los valores vacíos se convierten en null, excepto cuando se validan arrays o booleanos. Por ejemplo, si se envía una entrada de tipo text con un valor vacío, el resultado será null en lugar de una cadena vacía ("").
Para aplicar una unión de diferentes validadores, utiliza el envoltorio z.discriminatedUnion() para limitar el tipo basado en un campo de formulario específico. Este ejemplo acepta el envío de un formulario para "crear" o "actualizar" un usuario, utilizando el campo de formulario con el nombre type para determinar qué objeto validar:
import { defineAction } from 'astro:actions';import { z } from 'astro/zod';
export const server = { changeUser: defineAction({ accept: 'form', input: z.discriminatedUnion('type', [ z.object({ // Matches when the `type` field has the value `create` type: z.literal('create'), name: z.string(), email: z.email(), }), z.object({ // Matches when the `type` field has the value `update` type: z.literal('update'), id: z.number(), name: z.string(), email: z.email(), }), ]), async handler(input) { if (input.type === 'create') { // input is { type: 'create', name: string, email: string } } else { // input is { type: 'update', id: number, name: string, email: string } } }, }),};Validación de datos de formulario
Sección titulada “Validación de datos de formulario”Las acciones analizarán los datos de formulario enviados a un objeto, utilizando el valor del atributo name de cada entrada como las claves del objeto. Por ejemplo, un formulario que contiene <input name="search"> se analizará en un objeto como { search: 'user input' }. El esquema input de tu acción se utilizará para validar este objeto.
Para recibir el objeto FormData sin procesar en el manejador de tu acción en lugar de un objeto analizado, omite la propiedad input en la definición de tu acción.
El siguiente ejemplo muestra un formulario de registro de boletín validado que acepta el correo electrónico de un usuario y requiere una casilla de verificación de acuerdo de "términos de servicio".
-
Crea un componente de formulario HTML con atributos
nameúnicos en cada entrada:src/components/Newsletter.astro <form><label for="email">E-mail</label><input id="email" required type="email" name="email" /><label><input required type="checkbox" name="terms">I agree to the terms of service</label><button>Sign up</button></form> -
Define una acción
newsletterpara manejar el formulario enviado. Valida el campoemailutilizando el validadorz.email(), y la casilla de verificacióntermsutilizandoz.boolean():src/actions/index.ts import { defineAction } from 'astro:actions';import { z } from 'astro/zod';export const server = {newsletter: defineAction({accept: 'form',input: z.object({email: z.email(),terms: z.boolean(),}),handler: async ({ email, terms }) => { /* ... */ },})}Consulta la referencia de la API deinputpara ver todos los validadores de formularios disponibles. -
Agrega un
<script>al formulario HTML para enviar la entrada del usuario. Este ejemplo anula el comportamiento de envío predeterminado del formulario para llamar aactions.newsletter()y redirige a/confirmationutilizando la funciónnavigate():src/components/Newsletter.astro <form>7 collapsed lines<label for="email">E-mail</label><input id="email" required type="email" name="email" /><label><input required type="checkbox" name="terms">I agree to the terms of service</label><button>Sign up</button></form><script>import { actions } from 'astro:actions';import { navigate } from 'astro:transitions/client';const form = document.querySelector('form');form?.addEventListener('submit', async (event) => {event.preventDefault();const formData = new FormData(form);const { error } = await actions.newsletter(formData);if (!error) navigate('/confirmation');})</script>Consulta “Llamar acciones desde una acción de formulario HTML” para conocer una forma alternativa de enviar datos de formulario.
Mostrar errores de entrada de formulario
Sección titulada “Mostrar errores de entrada de formulario”Puedes validar las entradas del formulario antes del envío utilizando atributos de validación nativos de formularios HTML como required, type="email" y pattern. Para una validación de input más compleja en el backend, puedes utilizar la función de utilidad isInputError() proporcionada.
Para recuperar errores de entrada, utiliza la utilidad isInputError() para verificar si un error fue causado por una entrada no válida. Los errores de entrada contienen un objeto fields con mensajes para cada nombre de entrada que no se pudo validar. Puedes utilizar estos mensajes para indicarle a tu usuario que corrija su envío.
El siguiente ejemplo verifica el error con isInputError(), luego verifica si el error está en el campo email, antes de finalmente crear un mensaje a partir de los errores. Puedes usar la manipulación del DOM de JavaScript o tu framework de UI preferido para mostrar este mensaje a los usuarios.
import { actions, isInputError } from 'astro:actions';
const form = document.querySelector('form');const formData = new FormData(form);const { error } = await actions.newsletter(formData);if (isInputError(error)) { // Handle input errors. if (error.fields.email) { const message = error.fields.email.join(', '); }}Llamar acciones desde una acción de formulario HTML
Sección titulada “Llamar acciones desde una acción de formulario HTML”Las páginas deben renderizarse bajo demanda al llamar acciones utilizando una acción de formulario. Asegúrate de que el prerenderizado esté deshabilitado en la página antes de usar esta API.
Puedes habilitar envíos de formularios sin JavaScript con atributos estándar en cualquier elemento <form>. Los envíos de formularios sin JavaScript del lado del cliente pueden ser útiles como respaldo para cuando JavaScript no se carga, o si prefieres manejar los formularios por completo desde el servidor.
Llamar a Astro.getActionResult() en el servidor devuelve el resultado de tu envío de formulario (data o error), y se puede usar para redirigir dinámicamente, manejar errores de formulario, actualizar la interfaz de usuario y más.
Para llamar a una acción desde un formulario HTML, agrega method="POST" a tu <form>, luego establece el atributo action del formulario utilizando tu acción, por ejemplo action={actions.logout}. Esto configurará el atributo action para usar una cadena de consulta (query string) que el servidor maneja automáticamente.
Por ejemplo, este componente de Astro llama a la acción logout cuando se hace clic en el botón y vuelve a cargar la página actual:
---import { actions } from 'astro:actions';---
<form method="POST" action={actions.logout}> <button>Log out</button></form>Es posible que se requieran atributos adicionales en el elemento <form> para una validación de esquema adecuada con Zod. Por ejemplo, para incluir la subida de archivos, añade enctype="multipart/form-data" para asegurarte de que los archivos se envíen en un formato correctamente reconocido por z.instanceof(File):
---import { actions } from 'astro:actions';---<form method="POST" action={actions.upload} enctype="multipart/form-data" > <label for="file">Upload File</label> <input type="file" id="file" name="file" /> <button type="submit">Submit</button></form>Redirigir cuando la acción tenga éxito
Sección titulada “Redirigir cuando la acción tenga éxito”Si necesitas redirigir a una nueva ruta cuando la acción tenga éxito, puedes usar el resultado de una acción en el servidor. Un ejemplo común es crear un registro de producto y redirigir a la página del nuevo producto, p. ej. /products/[id].
Por ejemplo, supongamos que tienes una acción createProduct que devuelve el id del producto generado:
import { defineAction } from 'astro:actions';import { z } from 'astro/zod';
export const server = { createProduct: defineAction({ accept: 'form', input: z.object({ /* ... */ }), handler: async (input) => { const product = await persistToDatabase(input); return { id: product.id }; }, })}Puedes recuperar el resultado de la acción desde tu componente Astro llamando a Astro.getActionResult(). Esto devuelve un objeto que contiene las propiedades data o error cuando se llama a una acción, o undefined si la acción no fue llamada durante esta solicitud.
Utiliza la propiedad data para construir una URL que puedas usar con Astro.redirect():
---import { actions } from 'astro:actions';
const result = Astro.getActionResult(actions.createProduct);if (result && !result.error) { return Astro.redirect(`/products/${result.data.id}`);}---
<form method="POST" action={actions.createProduct}> <!--...--></form>Manejar errores de acción de formulario
Sección titulada “Manejar errores de acción de formulario”Llamar a Astro.getActionResult() en el componente de Astro que contiene tu formulario te da acceso a los objetos data y error para el manejo personalizado de errores.
El siguiente ejemplo muestra un mensaje de error general cuando falla una acción de newsletter:
---import { actions } from 'astro:actions';
const result = Astro.getActionResult(actions.newsletter);---
{result?.error && ( <p class="error">Unable to sign up. Please try again later.</p>)}<form method="POST" action={actions.newsletter}> <label> E-mail <input required type="email" name="email" /> </label> <button>Sign up</button></form>Para una mayor personalización, puedes usar la utilidad isInputError() para verificar si un error es causado por una entrada no válida.
El siguiente ejemplo muestra un banner de error debajo del campo de entrada email cuando se envía un correo electrónico no válido:
---import { actions, isInputError } from 'astro:actions';
const result = Astro.getActionResult(actions.newsletter);const inputErrors = isInputError(result?.error) ? result.error.fields : {};---
<form method="POST" action={actions.newsletter}> <label> E-mail <input required type="email" name="email" aria-describedby="error" /> </label> {inputErrors.email && <p id="error">{inputErrors.email.join(',')}</p>} <button>Sign up</button></form>Preservar los valores de entrada al producirse un error
Sección titulada “Preservar los valores de entrada al producirse un error”Los campos de entrada se limpiarán cada vez que se envíe un formulario. Para persistir los valores de entrada, puedes habilitar las transiciones de vista y aplicar la directiva transition:persist a cada entrada:
<input transition:persist required type="email" name="email" />Actualizar la interfaz de usuario con el resultado de una acción de formulario
Sección titulada “Actualizar la interfaz de usuario con el resultado de una acción de formulario”Para utilizar el valor de retorno de una acción para mostrar una notificación al usuario en caso de éxito, pasa la acción a Astro.getActionResult(). Utiliza la propiedad data devuelta para renderizar la interfaz de usuario que deseas mostrar.
Este ejemplo utiliza la propiedad productName devuelta por una acción addToCart para mostrar un mensaje de éxito.
---import { actions } from 'astro:actions';
const result = Astro.getActionResult(actions.addToCart);---
{result && !result.error && ( <p class="success">Added {result.data.productName} to cart</p>)}
<!--...-->Avanzado: Persistir los resultados de la acción con una sesión
Sección titulada “Avanzado: Persistir los resultados de la acción con una sesión”Añadido en:
astro@5.0.0
Los resultados de la acción se muestran como un envío POST. Esto significa que el resultado se restablecerá a undefined cuando un usuario cierre y vuelva a visitar la página. El usuario también verá un cuadro de diálogo "¿confirmar el reenvío del formulario?" si intenta actualizar la página.
Para personalizar este comportamiento, puedes agregar middleware para manejar el resultado de la acción manualmente. Puedes elegir persistir el resultado de la acción utilizando una cookie o el almacenamiento de sesión.
Comienza creando un archivo de middleware e importando la utilidad getActionContext() desde astro:actions. Esta función devuelve un objeto action con información sobre la solicitud de acción entrante, incluido el manejador de la acción y si la acción se llamó desde un formulario HTML. getActionContext() también devuelve las funciones setActionResult() y serializeActionResult() para establecer mediante programación el valor devuelto por Astro.getActionResult():
import { defineMiddleware } from 'astro:middleware';import { getActionContext } from 'astro:actions';
export const onRequest = defineMiddleware(async (context, next) => { const { action, setActionResult, serializeActionResult } = getActionContext(context); if (action?.calledFrom === 'form') { const result = await action.handler(); // ... handle the action result setActionResult(action.name, serializeActionResult(result)); } return next();});Una práctica común para persistir los resultados de los formularios HTML es el patrón POST / Redirección / GET. Esta redirección elimina el cuadro de diálogo "¿confirmar el reenvío del formulario?" cuando se actualiza la página y permite que los resultados de la acción persistan durante toda la sesión del usuario.
Este ejemplo aplica el patrón POST / Redirección / GET a todos los envíos de formularios utilizando el almacenamiento de sesión con el adaptador de servidor de Netlify instalado. Los resultados de la acción se escriben en un almacenamiento de sesión utilizando Netlify Blob y se recuperan después de una redirección utilizando un ID de sesión:
import { defineMiddleware } from 'astro:middleware';import { getActionContext } from 'astro:actions';import { randomUUID } from "node:crypto";import { getStore } from "@netlify/blobs";
export const onRequest = defineMiddleware(async (context, next) => { // Skip requests for prerendered pages if (context.isPrerendered) return next();
const { action, setActionResult, serializeActionResult } = getActionContext(context); // Create a Blob store to persist action results with Netlify Blob const actionStore = getStore("action-session");
// If an action result was forwarded as a cookie, set the result // to be accessible from `Astro.getActionResult()` const sessionId = context.cookies.get("action-session-id")?.value; const session = sessionId ? await actionStore.get(sessionId, { type: "json", }) : undefined;
if (session) { setActionResult(session.actionName, session.actionResult);
// Optional: delete the session after the page is rendered. // Feel free to implement your own persistence strategy await actionStore.delete(sessionId); context.cookies.delete("action-session-id"); return next(); }
// If an action was called from an HTML form action, // call the action handler and redirect to the destination page if (action?.calledFrom === "form") { const actionResult = await action.handler();
// Persist the action result using session storage const sessionId = randomUUID(); await actionStore.setJSON(sessionId, { actionName: action.name, actionResult: serializeActionResult(actionResult), });
// Pass the session ID as a cookie // to be retrieved after redirecting to the page context.cookies.set("action-session-id", sessionId);
// Redirect back to the previous page on error if (actionResult.error) { const referer = context.request.headers.get("Referer"); if (!referer) { throw new Error( "Internal: Referer unexpectedly missing from Action POST request.", ); } return context.redirect(referer); } // Redirect to the destination page on success return context.redirect(context.originPathname); }
return next();});Seguridad al usar acciones
Sección titulada “Seguridad al usar acciones”Las acciones son accesibles como endpoints públicos basados en el nombre de la acción. Por ejemplo, la acción blog.like() será accesible desde /_actions/blog.like. Esto es útil para realizar pruebas unitarias de los resultados de las acciones y depurar errores de producción. Sin embargo, esto significa que debes utilizar las mismas comprobaciones de autorización que considerarías para los endpoints de API y las páginas renderizadas bajo demanda.
Autorizar usuarios desde un manejador de acción
Sección titulada “Autorizar usuarios desde un manejador de acción”Para autorizar solicitudes de acciones, agrega una verificación de autenticación a tu manejador de acción. Es posible que desees utilizar una biblioteca de autenticación para gestionar las sesiones y la información de los usuarios.
Las acciones exponen un subconjunto del objeto APIContext para acceder a las propiedades pasadas desde el middleware utilizando context.locals. Cuando un usuario no está autorizado, puedes lanzar un ActionError con el código UNAUTHORIZED:
import { defineAction, ActionError } from 'astro:actions';
export const server = { getUserSettings: defineAction({ handler: async (_input, context) => { if (!context.locals.user) { throw new ActionError({ code: 'UNAUTHORIZED' }); } return { /* data on success */ }; } })}Limitar acciones desde el middleware
Sección titulada “Limitar acciones desde el middleware”Añadido en:
astro@5.0.0
Astro recomienda autorizar las sesiones de usuario desde tu manejador de acción para respetar los niveles de permiso y la limitación de velocidad por acción. Sin embargo, también puedes limitar las solicitudes a todas las acciones (o un subconjunto de acciones) desde el middleware.
Utiliza la función getActionContext() desde tu middleware para recuperar información sobre las solicitudes de acciones entrantes. Esto incluye el nombre de la acción y si esa acción se llamó mediante una función de llamada a procedimiento remoto (RPC) del lado del cliente (p. ej., actions.blog.like()) o un formulario HTML.
El siguiente ejemplo rechaza todas las solicitudes de acciones que no tienen un token de sesión válido. Si la verificación falla, se devuelve una respuesta "Prohibida" (Forbidden). Nota: este método garantiza que las acciones solo sean accesibles cuando una sesión está presente, pero no es un sustituto de una autorización segura.
import { defineMiddleware } from "astro:middleware";import { getActionContext } from "astro:actions";
export const onRequest = defineMiddleware(async (context, next) => { const { action } = getActionContext(context); // Check if the action was called from a client-side function if (action?.calledFrom === "rpc") { // If so, check for a user session token if (!context.cookies.has("user-session")) { return new Response("Forbidden", { status: 403 }); } }
context.cookies.set("user-session", "session-token-value"); return next();});Llamar acciones desde componentes de Astro y endpoints de servidor
Sección titulada “Llamar acciones desde componentes de Astro y endpoints de servidor”Puedes llamar acciones directamente desde los scripts de componentes de Astro utilizando el envoltorio Astro.callAction() (o context.callAction() al utilizar un endpoint de servidor). Esto es común para reutilizar la lógica de tus acciones en otro código del servidor.
Pasa la acción como primer argumento y cualquier parámetro de entrada como segundo argumento. Esto devuelve los mismos objetos data y error que recibes al llamar acciones en el cliente:
---import { actions } from 'astro:actions';
const searchQuery = Astro.url.searchParams.get('search');if (searchQuery) { const { data, error } = await Astro.callAction(actions.findProduct, { query: searchQuery }); // handle result}---