A Developer's Guide to Astro Actions in the AstroPress Theme

Bilal Mansouri
Astro Actions in the AstroPress

Astro is rapidly evolving from a static-site generator into a full-fledged web framework. One of the most powerful features driving this evolution is Astro Actions, a feature that dramatically simplifies the process of executing server-side code in response to user interactions.

The AstroPress theme is built on this modern, secure foundation. Instead of relying on cumbersome, standalone API endpoints for tasks like form submissions, AstroPress leverages Astro Actions to create a seamless, type-safe, and highly secure developer experience.

This technical guide is for developers who want to understand how AstroPress uses Actions and how they can extend this pattern for their own custom features.

What Are Astro Actions?

Before diving into the AstroPress implementation, let’s quickly define what Astro Actions are. In essence, Actions are a way to define server-side functions that can be called directly from your client-side code with end-to-end type safety.

Astro handles all the boilerplate that you would typically have to write yourself:

  • It creates and exposes a dedicated API endpoint for the action.
  • It handles data serialization and deserialization (e.g., from a FormData object to a typed object).
  • It provides a simple, unified way to handle success and error states.
  • Most importantly, it generates type-safe client-side helpers so you can call your server code as if it were a local function.

This eliminates the need for manual fetch calls, API route definitions, and data validation boilerplate, allowing you to focus purely on the logic.

A Deep Dive into AstroPress’s leads Action

The primary use case for Astro Actions in AstroPress is handling form submissions, such as from the contact form or newsletter signups. All of this logic is defined in a single file: /src/actions/index.ts.

Let’s dissect the leads action from that file:

// src/actions/index.ts
import { defineAction, ActionError } from 'astro:actions';
import { z } from 'astro:schema';
import { BLOG_API_KEY } from 'astro:env/server';

export const server = {
  leads: defineAction({
    accept: 'form',
    input: z.object({
      email: z.string().email('Please enter a valid email address'),
      name: z.string().min(2, 'Please enter at least 2 characters').optional(),
      message: z.string().min(10, 'Please enter at least 10 characters').optional(),
      source: z.string().default('website'),
    }),
    handler: async (input, context) => {
      // ... server-side logic ...
    }
  })
};

defineAction

This is the core function from astro:actions. It takes an object that defines the action’s configuration and behavior.

accept: 'form'

This property tells Astro that this action is designed to receive data from an HTML form. Astro will automatically parse the incoming FormData object and prepare it for validation.

input: z.object({...})

This is where the magic of type-safe validation happens. Astro Actions are integrated with Zod, a popular TypeScript-first validation library. By defining a Zod schema, you are telling Astro exactly what data to expect.

  • If the incoming data doesn’t match this schema (e.g., an invalid email format, a name that’s too short), Astro will automatically reject the request and send back a detailed validation error—before your handler code even runs.
  • This provides a robust security layer, ensuring that only valid, expected data ever reaches your core logic.

handler: async (input, context) => { ... }

This is the heart of your action—the server-side code that executes when the action is called with valid data. The handler receives two arguments:

  1. input: The validated and typed data from the client. Thanks to Zod and Astro, you can be confident that input.email is a valid email string, input.name is a string with at least 2 characters (if provided), etc.
  2. context: An object containing information about the request, such as headers and cookies.

Securely Using Environment Variables

Inside the handler, you’ll notice this line:

import { BLOG_API_KEY } from 'astro:env/server';

And its usage:

headers: {
  'Authorization': `Bearer ${BLOG_API_KEY}`
}

Astro provides a secure way to handle environment variables. By importing from astro:env/server, you are ensuring that BLOG_API_KEY is only available on the server. It will never be bundled or exposed to the client-side, which is critical for protecting secrets.

In this case, the action acts as a secure proxy, forwarding the lead data to a backend API (https://astropress-apis.mcpsplayground.workers.dev/api/leads) and safely authenticating the request with a secret key.

Error Handling with ActionError

If something goes wrong (e.g., the backend API is down), the action throws a structured ActionError. This is the standard way to communicate failures back to the client. Astro catches this error and sends a consistent JSON response that your client-side code can easily interpret.

Calling the Action from the Client

Defining the action is only half the story. How is it called from the frontend? Let’s look at a simplified example from /src/components/forms/ContactForm.astro.

<form id="contact-form">
  <!-- form fields -->
  <button type="submit">Send Message</button>
</form>

<script>
  import { actions } from 'astro:actions';

  const formElement = document.getElementById('contact-form');

  formElement.addEventListener('submit', async (event) => {
    event.preventDefault();
    
    const formData = new FormData(formElement);
    const { data, error } = await actions.leads(formData);

    if (error) {
      // Handle validation or server errors
      console.error(error.message);
      return;
    }

    // Handle success
    console.log(data.message);
  });
</script>

Here’s the breakdown:

  1. import { actions } from 'astro:actions';: Astro automatically generates this virtual module. The actions object is a fully type-safe client that contains all the actions you defined in src/actions/index.ts.
  2. await actions.leads(formData): This is the magic. You call your server-side action as if it were a local asynchronous function. You pass the FormData object directly, and Astro handles the rest.
  3. const { data, error } = ...: The call returns an object with two possible properties:
    • data: If the action was successful, this will contain the object returned from your handler.
    • error: If the action failed (due to validation or an ActionError), this will contain a structured error object.

This client-side experience is incredibly clean and robust. You get full IntelliSense and type-checking in your editor, and the error-handling pattern is simple and predictable.

Why This Pattern is a Win for Developers

The Astro Actions pattern used in AstroPress offers several key advantages over traditional API routes:

  • End-to-End Type Safety: From the Zod schema on the server to the generated client in your component, you get type safety across the entire stack, catching bugs at build time, not runtime.
  • Reduced Boilerplate: No more writing fetch wrappers, setting headers, parsing JSON, or manually creating API routes. Astro does the heavy lifting.
  • Integrated Validation: Built-in Zod validation provides a strong security guarantee and simplifies your handler logic.
  • Secure by Default: The architecture naturally encourages best practices, like keeping secrets on the server and validating all client input.
  • Improved Developer Experience: Having the action definition and the client-side call be so closely related, with full type inference, makes development faster and more enjoyable.

Conclusion

Astro Actions are a cornerstone of the modern, full-stack capabilities of the Astro framework. The AstroPress theme embraces this feature to provide a form submission system that is not only powerful and flexible but also secure and a joy to work with from a developer’s perspective.

By understanding this pattern, you can easily customize the existing form behavior or confidently add your own server-side actions for new features, knowing you are building on a solid, secure, and type-safe foundation.

About the Author

Bilal Mansouri - Author

Bilal Mansouri

Creator of AstroPress — the fastest, most secure blogging system built for performance and simplicity.