コンテンツのクエリ
EmDashは、Astroのページやコンポーネントでコンテンツを取得するためのクエリ関数を提供します。これらの関数はAstroのライブコンテンツコレクションパターンに従い、エラーハンドリングを含む構造化された結果を返します。
EmDashは主に2つのクエリ関数をエクスポートします:
| 関数 | 目的 | 戻り値 |
|---|---|---|
getEmDashCollection | コンテンツタイプの全エントリーを取得 | { entries, error } |
getEmDashEntry | IDまたはスラッグで単一エントリーを取得 | { entry, error, isPreview } |
これらをemdashからインポートします:
import { getEmDashCollection, getEmDashEntry } from "emdash";全エントリーの取得
Section titled “全エントリーの取得”コンテンツタイプの全エントリーを取得するにはgetEmDashCollectionを使用します:
astro title="src/pages/posts.astro"---import { getEmDashCollection } from "emdash";
const { entries: posts, error } = await getEmDashCollection("posts");
if (error) { console.error("Failed to load posts:", error);}---
<ul> {posts.map((post) => ( <li>{post.data.title}</li> ))}</ul>ロケールによるフィルタリング
Section titled “ロケールによるフィルタリング”i18nが有効な場合、特定の言語のコンテンツを取得するためにロケールでフィルタリングします:
// French postsconst { entries: frenchPosts } = await getEmDashCollection("posts", { locale: "fr", status: "published",});
// 現在のリクエストロケールを使用const { entries: localizedPosts } = await getEmDashCollection("posts", { locale: Astro.currentLocale, status: "published",});単一エントリーの場合は、localeを第3引数として渡します:
const { entry: post } = await getEmDashEntry("posts", "my-post", { locale: Astro.currentLocale,});localeが省略された場合、リクエストの現在のロケールがデフォルトになります。要求されたロケールの翻訳が存在しない場合、フォールバックチェーンが適用されます。
ステータスによるフィルタリング
Section titled “ステータスによるフィルタリング”公開済みまたは下書きのコンテンツのみを取得します:
// Only published postsconst { entries: published } = await getEmDashCollection("posts", { status: "published",});
// 下書きのみconst { entries: drafts } = await getEmDashCollection("posts", { status: "draft",});結果数の制限
Section titled “結果数の制限”返されるエントリー数を制限します:
// Get the 5 most recent postsconst { entries: recentPosts } = await getEmDashCollection("posts", { status: "published", limit: 5,});タクソノミーによるフィルタリング
Section titled “タクソノミーによるフィルタリング”カテゴリ、タグ、またはカスタムタクソノミータームでエントリーをフィルタリングします:
// Posts in the "news" categoryconst { entries: newsPosts } = await getEmDashCollection("posts", { status: "published", where: { category: "news" },});
// "javascript"タグを持つ投稿const { entries: jsPosts } = await getEmDashCollection("posts", { status: "published", where: { tag: "javascript" },});
// 複数タームのいずれかに一致する投稿const { entries: featuredNews } = await getEmDashCollection("posts", { status: "published", where: { category: ["news", "featured"] },});whereフィルターは、単一のタクソノミーに対して複数の値が提供された場合、ORロジックを使用します。
エラーハンドリング
Section titled “エラーハンドリング”信頼性が重要な場合は常にエラーをチェックしてください:
const { entries: posts, error } = await getEmDashCollection("posts");
if (error) { // ログ出力と適切な処理 console.error("投稿の読み込みに失敗しました:", error); return new Response("サーバーエラー", { status: 500 });}単一エントリーの取得
Section titled “単一エントリーの取得”IDまたはスラッグで1つのエントリーを取得するにはgetEmDashEntryを使用します:
astro title="src/pages/posts/[slug].astro"---import { getEmDashEntry } from "emdash";import { PortableText } from "emdash/ui";
const { slug } = Astro.params;const { entry: post, error } = await getEmDashEntry("posts", slug);
if (error) { return new Response("サーバーエラー", { status: 500 });}
if (!post) { return Astro.redirect("/404");}---
<article> <h1>{post.data.title}</h1> <PortableText value={post.data.content} /></article>エントリーの戻り値の型
Section titled “エントリーの戻り値の型”getEmDashEntryは結果オブジェクトを返します:
interface EntryResult<T> { entry: ContentEntry<T> | null; // null if not found error?: Error; // Only set for actual errors (not "not found") isPreview: boolean; // true if viewing preview/draft content}
interface ContentEntry<T> { id: string; data: T; edit: EditProxy; // Visual editing annotations}entry内のdataオブジェクトには、コンテンツタイプに対して定義されたすべてのフィールドが含まれます。editプロキシはビジュアル編集のアノテーションを提供します(以下を参照)。
プレビューモード
Section titled “プレビューモード”EmDashはミドルウェアを介してプレビューを自動的に処理します。URLに有効な_previewトークンが含まれている場合、ミドルウェアはそれを検証し、リクエストコンテキストを設定します。その後、クエリ関数は特別なパラメータなしで下書きコンテンツを提供します:
astro title="src/pages/posts/[...slug].astro"---import { getEmDashEntry } from "emdash";
const { slug } = Astro.params;
// 特別なプレビュー処理は不要 — ミドルウェアが自動的に処理しますconst { entry, isPreview, error } = await getEmDashEntry("posts", slug);
if (error) { return new Response("サーバーエラー", { status: 500 });}
if (!entry) { return Astro.redirect("/404");}---
{isPreview && ( <div class="preview-banner"> Viewing preview. This content is not published. </div>)}
<article> <h1>{entry.data.title}</h1> <PortableText value={entry.data.content} /></article>ビジュアル編集
Section titled “ビジュアル編集”クエリ関数によって返されるすべてのエントリーには、テンプレートにアノテーションを付けるためのeditプロキシが含まれています。これを要素にスプレッドして、認証済み編集者のインライン編集を有効にします:
<article {...entry.edit}> <h1 {...entry.edit.title}>{entry.data.title}</h1> <div {...entry.edit.content}> <PortableText value={entry.data.content} /> </div></article>編集モードでは、{...entry.edit.title}はビジュアル編集ツールバーがインライン編集を有効にするために使用するdata-emdash-ref属性を生成します。本番環境では、プロキシのスプレッドは何も出力しません — ランタイムコストはゼロです。
結果のソート
Section titled “結果のソート”getEmDashCollectionはソート順を保証しません。テンプレート内で結果をソートしてください:
const { entries: posts } = await getEmDashCollection("posts", { status: "published",});
// 公開日でソート、新しい順const sorted = posts.sort( (a, b) => (b.data.publishedAt?.getTime() ?? 0) - (a.data.publishedAt?.getTime() ?? 0),);一般的なソートパターン
Section titled “一般的なソートパターン”// Alphabetical by titleposts.sort((a, b) => a.data.title.localeCompare(b.data.title));
// カスタム順序フィールドでposts.sort((a, b) => (a.data.order ?? 0) - (b.data.order ?? 0));
// ランダム順序posts.sort(() => Math.random() - 0.5);TypeScript型
Section titled “TypeScript型”コレクションのTypeScript型を生成します:
npx emdash typesこれにより、各コレクションのインターフェースを含む.emdash/types.tsが作成されます。型安全性のために使用します:
import { getEmDashCollection, getEmDashEntry } from "emdash";import type { Post } from "../../.emdash/types";
// 型安全なコレクションクエリconst { entries: posts } = await getEmDashCollection<Post>("posts");// postsはContentEntry<Post>[]
// 型安全なエントリークエリconst { entry: post } = await getEmDashEntry<Post>("posts", "my-post");// postはContentEntry<Post> | null静的レンダリングとサーバーレンダリング
Section titled “静的レンダリングとサーバーレンダリング”EmDashコンテンツは、静的ページとサーバーレンダリングページの両方で動作します。
静的(事前レンダリング)
Section titled “静的(事前レンダリング)”静的ページでは、ビルド時にルートを生成するためにgetStaticPathsを使用します:
astro title="src/pages/posts/[slug].astro"---import { getEmDashCollection, getEmDashEntry } from "emdash";
export async function getStaticPaths() { const { entries: posts } = await getEmDashCollection("posts", { status: "published", });
return posts.map((post) => ({ params: { slug: post.data.slug }, }));}
const { slug } = Astro.params;const { entry: post } = await getEmDashEntry("posts", slug);---サーバーレンダリング
Section titled “サーバーレンダリング”サーバーレンダリングページでは、コンテンツを直接クエリします:
astro title="src/pages/posts/[slug].astro"---export const prerender = false;
import { getEmDashEntry } from "emdash";
const { slug } = Astro.params;const { entry: post, error } = await getEmDashEntry("posts", slug);
if (error) { return new Response("Server error", { status: 500 });}
if (!post) { return new Response(null, { status: 404 });}---パフォーマンスに関する考慮事項
Section titled “パフォーマンスに関する考慮事項”EmDashはAstroのライブコンテンツコレクションを使用しており、キャッシュは自動的に処理されます。サーバーレンダリングされたページでは、HTTPキャッシュヘッダーの追加を検討してください:
---const { entries: posts } = await getEmDashCollection("posts", { status: "published",});
// 5分間キャッシュAstro.response.headers.set("Cache-Control", "public, max-age=300");---冗長なクエリを避ける
Section titled “冗長なクエリを避ける”一度クエリを実行し、データをコンポーネントに渡します:
astro title="src/pages/index.astro"---import { getEmDashCollection } from "emdash";import PostList from "../../components/PostList.astro";import Sidebar from "../../components/Sidebar.astro";
// 一度だけクエリconst { entries: posts } = await getEmDashCollection("posts", { status: "published",});
const featured = posts.filter((p) => p.data.featured);const recent = posts.slice(0, 5);---
<PostList posts={featured} /><Sidebar posts={recent} />