ข้ามไปเนื้อหาหลัก

Category: reference

Zod — Schema Validation ที่ TypeScript รัก

Zod คือ library สำหรับ validate ข้อมูลแบบ type-safe ซึ่ง Astro Content Collections ใช้ภายใต้ฝาครอบ รู้จัก Zod ให้ดีคือรู้จัก schema ของ content collection ให้ดีขึ้น

· อ่านประมาณ 1 นาที

สารบัญ

ทำไมต้อง Zod

TypeScript ช่วย validate ประเภทข้อมูลตอน compile-time แต่ข้อมูลจากภายนอก (user input, API response, frontmatter) ไม่มีใครรับประกันว่า type จะถูกต้องตอน runtime — Zod แก้ปัญหานี้

พื้นฐาน

สร้าง Schema

import { z } from 'zod';

const UserSchema = z.object({
  name: z.string().min(1),
  age: z.number().int().positive(),
  email: z.string().email(),
  role: z.enum(['admin', 'user', 'guest']).default('user'),
});

type User = z.infer<typeof UserSchema>; // ดึง TypeScript type จาก schema

Parse และ Validate

// parse() — throw error ถ้าไม่ผ่าน
const user = UserSchema.parse(rawData);

// safeParse() — คืน { success, data } หรือ { success: false, error }
const result = UserSchema.safeParse(rawData);
if (result.success) {
  console.log(result.data); // typed ถูกต้อง
} else {
  console.log(result.error.flatten()); // error แบบอ่านง่าย
}

Patterns ที่ใช้บ่อยใน Astro Content Collections

Optional field พร้อม default

const schema = z.object({
  title: z.string(),
  tags: z.array(z.string()).default([]),
  status: z.enum(['draft', 'published']).default('draft'),
  image: z.string().optional(),   // ไม่ต้องมีก็ได้
});

Transform ค่าขณะ validate

const schema = z.object({
  date: z.string().transform(s => new Date(s)), // string → Date อัตโนมัติ
  slug: z.string().toLowerCase(),               // แปลงเป็น lowercase
});

Union type

const ContentSchema = z.discriminatedUnion('type', [
  z.object({ type: z.literal('article'), wordCount: z.number() }),
  z.object({ type: z.literal('video'), duration: z.number() }),
]);

Zod กับ Astro

Astro ใช้ Zod ใน src/content.config.ts โดยตรง — schema ที่เขียนนั้นคือ Zod schema เลย ดังนั้นความรู้ Zod ใช้ได้ทันที:

import { defineCollection, z } from 'astro:content';

const blog = defineCollection({
  schema: z.object({
    title: z.string(),
    date: z.date(),
    tags: z.array(z.string()).default([]),
  }),
});

ทรัพยากรเพิ่มเติม