Markdownを型安全に!AstroのContent Collections入門♪
ねつき
Markdownを型安全に!AstroのContent Collections入門♪
#Web
よくあるfrontmatterの悲劇
---
title: 今日の日記
date: 2025-12-10
tags: [日記, 雑談]
---
本文...
---
titl: 今日の日記 # ← タイポ!
date: '12月10日' # ← 形式が違う!
tag: [日記, 雑談] # ← tagsじゃなくてtag!
---
Content Collectionsの基本
// 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.coerce.date(),
tags: z.array(z.string()),
description: z.string().optional(),
}),
});
export const collections = { blog };
タイポしたらどうなる?
Error: blog → 2025-12-10.md frontmatter does not match collection schema.
"title" is required.
"date" must be a valid date.
"tags" is required.
コレクションからデータを取得
---
// src/pages/blog/index.astro
import { getCollection } from 'astro:content';
const posts = await getCollection('blog');
---
<h1>ブログ記事一覧</h1>
<ul>
{
posts.map((post) => (
<li>
<a href={`/blog/${post.id}/`}>{post.data.title}</a>
<time>{post.data.date.toLocaleDateString('ja-JP')}</time>
</li>
))
}
</ul>
フィルタリングもできる
---
import { getCollection } from 'astro:content';
// 下書きを除外
const publishedPosts = await getCollection('blog', ({ data }) => {
return data.draft !== true;
});
// 特定のタグだけ
const techPosts = await getCollection('blog', ({ data }) => {
return data.tags.includes('技術');
});
---
単一エントリの取得
---
// src/pages/blog/[...slug].astro
import { getEntry } from 'astro:content';
const { slug } = Astro.params;
const post = await getEntry('blog', slug);
if (!post) {
return Astro.redirect('/404');
}
const { Content } = await post.render();
---
<article>
<h1>{post.data.title}</h1>
<time>{post.data.date.toLocaleDateString('ja-JP')}</time>
<Content />
</article>
このサイトでの使用例
const diary = defineCollection({
loader: glob({ pattern: '**/*.md', base: './src/content/diary' }),
schema: z.object({
title: z.string(),
date: z.coerce.date(),
tags: z.array(z.string()),
description: z.string(),
}),
});
const diaryEn = defineCollection({
loader: glob({ pattern: '**/*.md', base: './src/content/diary-en' }),
schema: z.object({
title: z.string(),
date: z.coerce.date(),
tags: z.array(z.string()),
description: z.string(),
}),
});
Zodのよく使うバリデーション
| 書き方 | 意味 |
|---|---|
z.string() | 文字列(必須) |
z.string().optional() | 文字列(任意) |
z.string().default('デフォルト値') | デフォルト値付き |
z.coerce.date() | 日付(文字列から変換) |
z.array(z.string()) | 文字列の配列 |
z.boolean() | 真偽値 |
z.enum(['a', 'b', 'c']) | 列挙型 |
Astroシリーズまとめ
| 日付 | テーマ | 学んだこと |
|---|---|---|
| 12/8 | 基礎編 | コンポーネント、レイアウト、Islands |
| 12/9 | 動的ルーティング | [slug]、getStaticPaths |
| 12/10 | Content Collections | スキーマ定義、型安全なMarkdown |
♪ 拍手 ♪
0 拍手