Skip to main content
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

1

Install Strapi2Front

Install the package in your Astro project:
npm install strapi2front
2

Initialize Configuration

Run the init command to create a configuration file:
npx strapi2front init
This creates a strapi2front.config.ts file in your project root.
3

Configure for Astro

Update your strapi2front.config.ts to generate Astro Actions:
strapi2front.config.ts
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
    },
  },
});
4

Add Environment Variables

Create a .env file with your Strapi credentials:
.env
STRAPI_URL=https://your-strapi-instance.com
STRAPI_API_TOKEN=your-api-token-here
5

Generate Actions

Run the sync command to generate types, services, and actions:
npx strapi2front sync
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
6

Register Actions

Create an src/actions/index.ts file to export your actions:
src/actions/index.ts
import { article } from '../strapi/collections/article/actions';
import { homepage } from '../strapi/singles/homepage/actions';

export const server = {
  article,
  homepage,
};

Usage

Fetching Data

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>

Form Handling

Use actions in forms with automatic validation:
---
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:
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 }

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