コンテンツにスキップ

コンテンツのクエリ

EmDashは、Astroのページやコンポーネントでコンテンツを取得するためのクエリ関数を提供します。これらの関数はAstroのライブコンテンツコレクションパターンに従い、エラーハンドリングを含む構造化された結果を返します。

EmDashは主に2つのクエリ関数をエクスポートします:

関数目的戻り値
getEmDashCollectionコンテンツタイプの全エントリーを取得{ entries, error }
getEmDashEntryIDまたはスラッグで単一エントリーを取得{ entry, error, isPreview }

これらをemdashからインポートします:

import { getEmDashCollection, getEmDashEntry } from "emdash";

コンテンツタイプの全エントリーを取得するには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 posts
const { 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 posts
const { entries: published } = await getEmDashCollection("posts", {
status: "published",
});
// 下書きのみ
const { entries: drafts } = await getEmDashCollection("posts", {
status: "draft",
});

返されるエントリー数を制限します:

// Get the 5 most recent posts
const { entries: recentPosts } = await getEmDashCollection("posts", {
status: "published",
limit: 5,
});

タクソノミーによるフィルタリング

Section titled “タクソノミーによるフィルタリング”

カテゴリ、タグ、またはカスタムタクソノミータームでエントリーをフィルタリングします:

// Posts in the "news" category
const { 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ロジックを使用します。

信頼性が重要な場合は常にエラーをチェックしてください:

const { entries: posts, error } = await getEmDashCollection("posts");
if (error) {
// ログ出力と適切な処理
console.error("投稿の読み込みに失敗しました:", error);
return new Response("サーバーエラー", { status: 500 });
}

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>

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プロキシはビジュアル編集のアノテーションを提供します(以下を参照)。

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>

クエリ関数によって返されるすべてのエントリーには、テンプレートにアノテーションを付けるための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属性を生成します。本番環境では、プロキシのスプレッドは何も出力しません — ランタイムコストはゼロです。

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),
);
// Alphabetical by title
posts.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型を生成します:

Terminal window
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コンテンツは、静的ページとサーバーレンダリングページの両方で動作します。

静的ページでは、ビルド時にルートを生成するために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);
---

サーバーレンダリングページでは、コンテンツを直接クエリします:

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");
---

一度クエリを実行し、データをコンポーネントに渡します:

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} />