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

Category: guide

Astro Content Collections — คู่มือฉบับใช้งานจริง

คู่มือการใช้ Content Collections ใน Astro 6 สำหรับจัดการเนื้อหาแบบ type-safe ด้วย Zod schema พร้อม patterns ที่ใช้บ่อยในโปรเจคจริง

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

สารบัญ

Content Collections คืออะไร

Content Collections คือระบบจัดการเนื้อหาใน Astro ที่ให้คุณเขียน Markdown แล้วได้ TypeScript type safety มาฟรี — frontmatter ผิดรูปแบบ = build ไม่ผ่าน

การตั้งค่าใน Astro 6

ใน Astro 6 ไฟล์ config ย้ายมาที่ src/content.config.ts (ไม่ใช่ src/content/config.ts อีกต่อไป)

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

const blog = defineCollection({
  loader: glob({ pattern: '**/*.md', base: './src/content/blog' }),
  schema: z.object({
    title: z.string(),
    date: z.date(),
    tags: z.array(z.string()).default([]),
    status: z.enum(['draft', 'published']).default('draft'),
    image: z.string().optional(),
  }),
});

export const collections = { blog };

Query ข้อมูล

ดึงทั้งหมด

---
import { getCollection } from 'astro:content';

const posts = await getCollection('blog');
// กรอง draft ออก
const published = posts.filter(p => p.data.status === 'published');
// เรียงตามวันที่ล่าสุด
const sorted = published.sort((a, b) =>
  b.data.date.getTime() - a.data.date.getTime()
);
---

Dynamic routes (Static Paths)

---
// src/pages/blog/[slug].astro
import { getCollection, render } from 'astro:content';

export async function getStaticPaths() {
  const posts = await getCollection('blog');
  return posts.map(post => ({ params: { slug: post.id } }));
}

const { slug } = Astro.params;
const posts = await getCollection('blog');
const post = posts.find(p => p.id === slug);
if (!post) throw new Error(`Post not found: ${slug}`);

const { Content } = await render(post);
---

<article>
  <h1>{post.data.title}</h1>
  <Content />
</article>

Zod Schema Patterns ที่ใช้บ่อย

const schema = z.object({
  // Required
  title: z.string().min(1),
  date: z.date(),

  // Optional พร้อม default
  tags: z.array(z.string()).default([]),
  status: z.enum(['active', 'completed', 'archived']).default('active'),

  // Optional ไม่มี default
  image: z.string().optional(),
  url: z.string().url().optional(),

  // Coerce string → date (กรณี frontmatter เป็น string)
  updatedAt: z.coerce.date().optional(),
});

ข้อดีที่ได้จริง

  • TypeScript autocomplete ใน frontmatter และหน้า Astro
  • Build-time validation — frontmatter ผิด = error ชัดเจน ไม่ใช่ runtime bug
  • ไม่ต้องเขียน type แยกz.infer<typeof schema> ให้ type อัตโนมัติ
  • glob loader — เพิ่มไฟล์ markdown = เพิ่ม content ได้เลย ไม่ต้อง register

ข้อควรระวัง

YAML dates เป็น UTCdate: 2026-06-01 ใน frontmatter คือ UTC midnight ถ้า format โดยไม่ระบุ timezone จะแสดงวันผิดสำหรับ UTC+7 ให้เพิ่ม timeZone: 'Asia/Bangkok' ใน Intl.DateTimeFormat

Astro 6 ย้าย config — ถ้าอ่าน tutorial เก่า อาจเห็น src/content/config.ts ซึ่งใช้ไม่ได้ใน Astro 6 แล้ว