Astro Actions: Type-safe Server Functions

#astro13 เม.ย. 2569

Astro Actions คืออะไร

Astro Actions (Astro 4.15+) เป็น type-safe server functions ที่เรียกจาก client ได้โดยตรง คล้ายกับ Server Actions ใน Next.js

สร้าง Actions

// src/actions/index.ts
import { defineAction, z } from 'astro:actions';

export const server = {
  createPost: defineAction({
    input: z.object({
      title: z.string().min(1),
      content: z.string().min(10),
      tags: z.array(z.string())
    }),
    handler: async (input, context) => {
      const user = context.locals.user;
      if (!user) throw new ActionError({ code: 'UNAUTHORIZED' });

      const post = await db.insert(posts).values({
        ...input,
        authorId: user.id,
        createdAt: new Date()
      }).returning();

      return { post: post[0] };
    }
  }),

  deletePost: defineAction({
    input: z.object({ id: z.number() }),
    handler: async ({ id }, context) => {
      await db.delete(posts).where(eq(posts.id, id));
      return { success: true };
    }
  })
};

เรียกใช้จาก Client

---
import { actions } from 'astro:actions';
---

<form method="POST" action={actions.createPost}>
  <input name="title" />
  <textarea name="content"></textarea>
  <button type="submit">สร้างบทความ</button>
</form>

เรียกใช้ด้วย JavaScript

import { actions } from 'astro:actions';

async function handleSubmit(formData: FormData) {
  const { data, error } = await actions.createPost({
    title: formData.get('title') as string,
    content: formData.get('content') as string,
    tags: ['astro']
  });

  if (error) {
    console.error(error.message);
    return;
  }

  console.log('สร้างสำเร็จ:', data.post);
}

Error Handling

import { ActionError } from 'astro:actions';

handler: async (input) => {
  const existing = await db.query.posts.findFirst({
    where: eq(posts.title, input.title)
  });

  if (existing) {
    throw new ActionError({
      code: 'CONFLICT',
      message: 'มีบทความชื่อนี้แล้ว'
    });
  }
}

สรุป

Astro Actions ทำให้เขียน server logic ได้ง่ายและ type-safe โดยไม่ต้องสร้าง API endpoint แยกต่างหาก เหมาะกับ form submissions และ mutations ครับ