コンテンツのクエリ

このページ

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

クエリ関数

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

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

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

import { getEmDashCollection, getEmDashEntry } from "emdash";

すべてのエントリを取得

getEmDashCollection を使用して、コンテンツタイプのすべてのエントリを取得します:

---
import { getEmDashCollection } from "emdash";

const { entries: posts, error } = await getEmDashCollection("posts");

if (error) {
  console.error("投稿の読み込みに失敗しました:", error);
}
---

<ul>
  {posts.map((post) => (
    <li>{post.data.title}</li>
  ))}
</ul>

ロケールでフィルタリング

i18n が有効な場合、ロケールでフィルタリングして特定の言語のコンテンツを取得します:

// フランス語の投稿
const { entries: frenchPosts } = await getEmDashCollection("posts", {
	locale: "fr",
	status: "published",
});

// 現在のリクエストのロケールを使用
const { entries: localizedPosts } = await getEmDashCollection("posts", {
	locale: Astro.currentLocale,
	status: "published",
});

単一のエントリの場合、3番目の引数として locale を渡します:

const { entry: post } = await getEmDashEntry("posts", "my-post", {
	locale: Astro.currentLocale,
});

locale が省略された場合、リクエストの現在のロケールがデフォルトになります。リクエストされたロケールの翻訳が存在しない場合、フォールバックチェーンが適用されます。

ステータスでフィルタリング

公開済みまたは下書きのコンテンツのみを取得:

// 公開済みの投稿のみ
const { entries: published } = await getEmDashCollection("posts", {
	status: "published",
});

// 下書きのみ
const { entries: drafts } = await getEmDashCollection("posts", {
	status: "draft",
});

結果を制限

返されるエントリの数を制限:

// 最新の5件の投稿を取得
const { entries: recentPosts } = await getEmDashCollection("posts", {
	status: "published",
	limit: 5,
});

タクソノミーでフィルタリング

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

// "news" カテゴリの投稿
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 });
}

単一のエントリを取得

getEmDashEntry を使用して、ID またはスラッグでエントリを取得します:

---
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
	error?: Error; // 実際のエラーの場合のみ設定(「見つからない」ではない)
	isPreview: boolean; // プレビュー/下書きコンテンツを表示している場合は true
}

interface ContentEntry<T> {
	id: string;
	data: T;
	edit: EditProxy; // ビジュアル編集アノテーション
}

entry 内の data オブジェクトには、コンテンツタイプに定義されたすべてのフィールドが含まれます。edit プロキシはビジュアル編集アノテーションを提供します(下記参照)。

プレビューモード

EmDash はミドルウェアを介してプレビューを自動的に処理します。URL に有効な _preview トークンが含まれている場合、ミドルウェアはそれを検証し、リクエストコンテキストをセットアップします。クエリ関数は、特別なパラメータなしで下書きコンテンツを提供します:

---
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">
    プレビューを表示中。このコンテンツは公開されていません。
  </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),
);

一般的なソートパターン

// タイトルでアルファベット順
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 型

コレクションの 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

静的 vs. サーバーレンダリング

EmDash コンテンツは、静的ページとサーバーレンダリングされたページの両方で機能します。

静的(事前レンダリング)

静的ページの場合、getStaticPaths を使用してビルド時にルートを生成します:

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

サーバーレンダリング

サーバーレンダリングされたページの場合、コンテンツを直接クエリします:

---
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("サーバーエラー", { status: 500 });
}

if (!post) {
  return new Response(null, { status: 404 });
}
---

パフォーマンスの考慮事項

キャッシング

EmDash は Astro のライブコンテンツコレクションを使用し、キャッシングを自動的に処理します。サーバーレンダリングされたページの場合、HTTP キャッシュヘッダーの追加を検討してください:

---
const { entries: posts } = await getEmDashCollection("posts", {
  status: "published",
});

// 5分間キャッシュ
Astro.response.headers.set("Cache-Control", "public, max-age=300");
---

冗長なクエリを避ける

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

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

次のステップ