ข้ามไปยังเนื้อหา

Content Collections

ใน Next.js คุณมักใช้ contentlayer หรือ fs + gray-matter เพื่อโหลดไฟล์ MDX พร้อม type safety Astro มี content collections built-in: วางไฟล์ MD/MDX ใน src/content/<collection>/ กำหนด schema ด้วย zod ใน src/content.config.ts แล้วใช้ getCollection('blog') ที่ไหนก็ได้ในโปรเจกต์ — มี type safety เต็มรูปแบบ ไม่ต้องใช้ library ของ third-party

ต้องใช้โปรเจกต์ Astro เต็มรูปแบบ ใช้ npm create astro@latest เพื่อสร้างโปรเจกต์

React
// contentlayer.config.ts (Next.js)
import { defineDocumentType, makeSource } from 'contentlayer/source-files';
export const Post = defineDocumentType(() => ({
name: 'Post',
filePathPattern: 'blog/**/*.mdx',
contentType: 'mdx',
fields: {
title: { type: 'string', required: true },
date: { type: 'date', required: true },
tags: { type: 'list', of: { type: 'string' } },
},
}));
export default makeSource({ contentDirPath: 'content', documentTypes: [Post] });
// In a page:
// import { allPosts } from 'contentlayer/generated';
Astro
// src/content.config.ts (Astro)
import { defineCollection, z } from 'astro:content';
const blog = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
date: z.coerce.date(),
tags: z.array(z.string()).optional(),
}),
});
export const collections = { blog };
// In a page:
// import { getCollection } from 'astro:content';
// const posts = await getCollection('blog');

Schema อยู่ใน src/content.config.ts — Astro ตรวจสอบทุกไฟล์ใน collection ตอน build time และแสดง type errors ใน editor ของคุณ

src/content.config.ts
import { defineCollection, z } from 'astro:content';
const blog = defineCollection({
type: 'content', // 'content' for MD/MDX, 'data' for JSON/YAML
schema: z.object({
title: z.string(),
description: z.string().optional(),
date: z.coerce.date(),
draft: z.boolean().default(false),
tags: z.array(z.string()).default([]),
}),
});
export const collections = { blog };
src/pages/blog/index.astro
---
import { getCollection } from 'astro:content';
const posts = await getCollection('blog', ({ data }) => !data.draft);
const sorted = posts.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());
---
<html lang="en">
<head><meta charset="utf-8" /><title>Blog</title></head>
<body>
<h1>Blog</h1>
<ul>
{sorted.map((post) => (
<li>
<a href={`/blog/${post.slug}/`}>{post.data.title}</a>
<time>{post.data.date.toLocaleDateString()}</time>
</li>
))}
</ul>
</body>
</html>

argument ที่สองของ getCollection คือ optional filter function — ที่นี่ใช้กรอง drafts ออก post.data มี type จาก zod schema เต็มรูปแบบ

คุณกำหนด schema สำหรับ content collection ที่ไหน?
ดึงข้อมูลทุก entry จาก collection ชื่อ 'blog' ได้อย่างไร?
ข้อดีหลักของ content collections เทียบกับ `fs` + `gray-matter` คืออะไร?