Prerequisites :
Astro 4.0 or higher (Actions are only available in v4+)
TypeScript enabled in your Astro project
Strapi v4 or v5 backend
Strapi2Front generates type-safe Astro Actions that provide a seamless way to fetch and mutate Strapi content with built-in validation, error handling, and TypeScript support.
Features
Type-safe Actions — Full TypeScript support with auto-generated types
Zod Validation — Automatic input validation using Zod schemas
Error Handling — Built-in error handling with ActionError
CRUD Operations — Complete create, read, update, delete functionality
Slug Support — Automatic slug-based queries when available
Pagination — Built-in pagination support
Populate Relations — Easy relation population
Setup
Install Strapi2Front
Install the package in your Astro project:
Initialize Configuration
Run the init command to create a configuration file: This creates a strapi2front.config.ts file in your project root.
Configure for Astro
Update your strapi2front.config.ts to generate Astro Actions: import { defineConfig } from 'strapi2front' ;
export default defineConfig ({
strapiUrl: process . env . STRAPI_URL || 'http://localhost:1337' ,
strapiVersion: 'v5' , // or 'v4'
output: {
path: './src/strapi' ,
structure: 'by-feature' , // or 'by-type'
} ,
features: {
types: true ,
services: true ,
schemas: true ,
actions: {
enabled: true ,
framework: 'astro' ,
useTypedSchemas: true , // Use Zod schemas for validation
},
} ,
}) ;
Add Environment Variables
Create a .env file with your Strapi credentials: STRAPI_URL = https://your-strapi-instance.com
STRAPI_API_TOKEN = your-api-token-here
Generate Actions
Run the sync command to generate types, services, and actions: This generates the following structure: src/strapi/
├── collections/
│ └── article/
│ ├── types.ts # TypeScript types
│ ├── schemas.ts # Zod schemas
│ ├── service.ts # Service functions
│ └── actions.ts # Astro Actions
├── singles/
│ └── homepage/
│ ├── types.ts
│ ├── schemas.ts
│ ├── service.ts
│ └── actions.ts
└── shared/
├── client.ts # Strapi client
└── utils.ts # Utility types
Register Actions
Create an src/actions/index.ts file to export your actions: import { article } from '../strapi/collections/article/actions' ;
import { homepage } from '../strapi/singles/homepage/actions' ;
export const server = {
article ,
homepage ,
};
Usage
Fetching Data
Collection Type
Single Type
By Slug
Use actions to fetch collection data in your Astro components: src/pages/blog/index.astro
---
import { actions } from 'astro:actions' ;
// Get all articles with pagination
const { data : articles } = await actions . article . getAll ({
pagination: {
page: 1 ,
pageSize: 10 ,
},
sort: [ 'publishedAt:desc' ],
});
---
< div class = "blog-list" >
{ articles . data . map ( article => (
< article >
< h2 > { article . title } </ h2 >
< p > { article . excerpt } </ p >
< a href = { `/blog/ ${ article . slug } ` } > Read more </ a >
</ article >
)) }
</ div >
Fetch single type data: ---
import { actions } from 'astro:actions' ;
// Get homepage data
const { data : homepage } = await actions . homepage . get ({
populate: [ 'hero' , 'sections' ],
});
---
< section class = "hero" >
< h1 > { homepage . hero . title } </ h1 >
< p > { homepage . hero . description } </ p >
</ section >
Fetch by slug (when available): src/pages/blog/[slug].astro
---
import { actions } from 'astro:actions' ;
const { slug } = Astro . params ;
// Get article by slug
const { data : article } = await actions . article . getBySlug ({
slug: slug ! ,
populate: [ 'author' , 'categories' ],
});
if ( ! article ) {
return Astro . redirect ( '/404' );
}
---
< article >
< h1 > { article . title } </ h1 >
< div > { article . content } </ div >
</ article >
Use actions in forms with automatic validation:
src/pages/contact.astro
src/strapi/collections/contact/actions.ts
---
import { actions } from 'astro:actions' ;
const result = Astro . getActionResult ( actions . contact . create );
---
< form method = "POST" action = { actions . contact . create } >
< input type = "text" name = "name" placeholder = "Your name" required />
< input type = "email" name = "email" placeholder = "Email" required />
< textarea name = "message" placeholder = "Message" required ></ textarea >
{ result ?. error && (
< p class = "error" > { result . error . message } </ p >
) }
{ result ?. data && (
< p class = "success" > Thank you! We'll be in touch soon. </ p >
) }
< button type = "submit" > Send </ button >
</ form >
Client-Side Actions
Call actions from client-side scripts:
src/components/Newsletter.tsx
import { actions } from 'astro:actions' ;
import { useState } from 'react' ;
export function Newsletter () {
const [ email , setEmail ] = useState ( '' );
const [ status , setStatus ] = useState < 'idle' | 'loading' | 'success' | 'error' >( 'idle' );
const handleSubmit = async ( e : React . FormEvent ) => {
e . preventDefault ();
setStatus ( 'loading' );
try {
const { data , error } = await actions . newsletter . create ({
data: { email },
});
if ( error ) {
setStatus ( 'error' );
return ;
}
setStatus ( 'success' );
setEmail ( '' );
} catch ( error ) {
setStatus ( 'error' );
}
};
return (
< form onSubmit = { handleSubmit } >
< input
type = "email"
value = { email }
onChange = {(e) => setEmail (e.target.value)}
placeholder = "Enter your email"
required
/>
< button type = "submit" disabled = { status === 'loading' } >
{ status === ' loading ' ? 'Subscribing...' : 'Subscribe' }
</ button >
{ status === ' success ' && < p > Successfully subscribed !</ p >}
{ status === ' error ' && < p > Something went wrong . Please try again .</ p >}
</ form >
);
}
Generated Actions Reference
Collection Type Actions
For each collection type (e.g., Article), the following actions are generated:
getAll
getOne
getBySlug
create
update
delete
count
Fetch all items with pagination: await actions . article . getAll ({
pagination: {
page: 1 ,
pageSize: 25 ,
},
sort: [ 'publishedAt:desc' ],
});
Options:
pagination — Page-based or offset-based pagination
sort — Sort by field(s)
Returns: { data: Article[], pagination: StrapiPagination }
Fetch a single item by ID or documentId: // Strapi v5 (documentId)
await actions . article . getOne ({
documentId: 'abc123' ,
populate: [ 'author' , 'categories' ],
});
// Strapi v4 (numeric id)
await actions . article . getOne ({
id: 42 ,
populate: [ 'author' ],
});
Returns: Single Article or throws NOT_FOUND errorFetch by slug (only generated if collection has slug field): await actions . article . getBySlug ({
slug: 'my-first-post' ,
populate: [ 'author' ],
});
Returns: Single Article or throws NOT_FOUND errorCreate a new item: await actions . article . create ({
data: {
title: 'New Article' ,
content: 'Article content...' ,
publishedAt: new Date (). toISOString (),
},
});
Returns: Created ArticleUpdate an existing item: await actions . article . update ({
documentId: 'abc123' , // or id for v4
data: {
title: 'Updated Title' ,
},
});
Returns: Updated ArticleDelete an item: await actions . article . delete ({
documentId: 'abc123' , // or id for v4
});
Returns: { success: true }Count items with optional filters: await actions . article . count ({
filters: {
publishedAt: { $notNull: true },
},
});
Returns: { count: number }
Single Type Actions
For single types (e.g., Homepage), these actions are generated:
get() — Fetch the single type data
update() — Update the single type
Advanced Features
Typed Zod Schemas
When useTypedSchemas: true is enabled, Strapi2Front generates fully typed Zod schemas based on your Strapi content types:
// Auto-generated schema with proper validation
const createSchema = z . object ({
title: z . string (). min ( 1 ),
excerpt: z . string (). optional (),
content: z . string (),
publishedAt: z . string (). datetime (). optional (),
author: z . string (), // Relation
categories: z . array ( z . string ()). optional (), // Relation array
});
Error Handling
All actions include comprehensive error handling:
import { actions , isInputError } from 'astro:actions' ;
try {
const { data , error } = await actions . article . getOne ({
documentId: 'invalid-id' ,
});
if ( error ) {
if ( error . code === 'NOT_FOUND' ) {
console . log ( 'Article not found' );
} else if ( isInputError ( error )) {
console . log ( 'Validation error:' , error . fields );
}
}
} catch ( error ) {
console . error ( 'Unexpected error:' , error );
}
Populate Relations
Easily populate related content:
// Populate specific relations
await actions . article . getOne ({
documentId: 'abc123' ,
populate: [ 'author' , 'categories' ],
});
// Deep populate
await actions . article . getOne ({
documentId: 'abc123' ,
populate: {
author: {
populate: [ 'avatar' ],
},
categories: true ,
},
});
Best Practices
Always validate user input, even though Zod schemas provide automatic validation. Consider adding additional business logic validation when needed.
Use the findAll() method from services when you need all items without pagination. This is more efficient than manually paginating through all pages.
Actions are only available in Astro 4.0+. If you’re using an earlier version, use the generated services directly instead.
Troubleshooting
Actions not found
Make sure you’ve registered your actions in src/actions/index.ts and exported them from the server object.
TypeScript errors
Run npx strapi2front sync to regenerate types after making changes to your Strapi schema.
Validation errors
Check that your input data matches the generated Zod schemas. You can import schemas from collections/[name]/schemas.ts to validate data before submitting.
Next Steps