#10867 47877a7 Thanks @ematipico! - Adds experimental rewriting in Astro with a new rewrite() function and the middleware next() function.
The feature is available via an experimental flag in astro.config.mjs:
export default defineConfig({
experimental: {
rewriting: true,
},
});
When enabled, you can use rewrite() to render another page without changing the URL of the browser in Astro pages and endpoints.
---
// src/pages/dashboard.astro
if (!Astro.props.allowed) {
return Astro.rewrite('/');
}
---
// src/pages/api.js
export function GET(ctx) {
if (!ctx.locals.allowed) {
return ctx.rewrite('/');
}
}
The middleware next() function now accepts a parameter with the same type as the rewrite() function. For example, with next("/"), you can call the next middleware function with a new Request.
// src/middleware.js
export function onRequest(ctx, next) {
if (!ctx.cookies.get('allowed')) {
return next('/'); // new signature
}
return next();
}
NOTE: please read the RFC to understand the current expectations of the new APIs.
#10858 c0c509b Thanks @bholmesdev! - Adds experimental support for the Actions API. Actions let you define type-safe endpoints you can query from client components with progressive enhancement built in.
Actions help you write type-safe backend functions you can call from anywhere. Enable server rendering using the output property and add the actions flag to the experimental object:
{
output: 'hybrid', // or 'server'
experimental: {
actions: true,
},
}
Declare all your actions in src/actions/index.ts. This file is the global actions handler.
Define an action using the defineAction() utility from the astro:actions module. These accept the handler property to define your server-side request handler. If your action accepts arguments, apply the input property to validate parameters with Zod.
This example defines two actions: like and comment. The like action accepts a JSON object with a postId string, while the comment action accepts FormData with postId, author, and body strings. Each handler updates your database and return a type-safe response.
// src/actions/index.ts
import { defineAction, z } from 'astro:actions';
export const server = {
like: defineAction({
input: z.object({ postId: z.string() }),
handler: async ({ postId }, context) => {
// update likes in db
return likes;
},
}),
comment: defineAction({
accept: 'form',
input: z.object({
postId: z.string(),
body: z.string(),
}),
handler: async ({ postId }, context) => {
// insert comments in db
return comment;
},
}),
};
Then, call an action from your client components using the actions object from astro:actions. You can pass a type-safe object when using JSON, or a FormData object when using accept: 'form' in your action definition:
```tsx "actions"
// src/components/blog.tsx
import { actions } from 'astro:actions';
import { useState } from 'preact/hooks';
export function Like({ postId }: { postId: string }) {
const [likes, setLikes] = useState(0);
return (
<button
onClick={async () => {
const newLikes = await actions.like({ postId });
setLikes(newLikes);
}}
>
{likes} likes
</button>
);
}
export function Comment({ postId }: { postId: string }) {
return (
<form
onSubmit={async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const result = await actions.blog.comment(formData);
// handle result
}}
>
<input type="hidden" name="postId" value={postId} />
<label for="author">Author</label>
<input id="author" type="text" name="author" />
<textarea rows={10} name="body"></textarea>
<button type="submit">Post</button>
</form>
);
}
```
For a complete overview, and to give feedback on this experimental API, see the Actions RFC.