コンテンツのクエリ

このページ

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

クエリ関数

関数用途戻り値
getEmDashCollectionコンテンツタイプのすべてのエントリを取得{ entries, error }
getEmDashEntryID またはスラッグで単一のエントリを取得{ entry, error, isPreview }
import { getEmDashCollection, getEmDashEntry } from "emdash";

すべてのエントリを取得

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

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

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

結果を制限

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

タクソノミーでフィルター

const { entries: newsPosts } = await getEmDashCollection("posts", {
	status: "published",
	where: { category: "news" },
});

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

単一エントリの取得

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

エントリの戻り値型

interface EntryResult<T> {
	entry: ContentEntry<T> | null;
	error?: Error;
	isPreview: boolean;
}

interface ContentEntry<T> {
	id: string;
	data: T;
	edit: EditProxy;
}

SEO パネルデータのレンダリング

supports: ["seo"] を持つコレクションでは、エディターはアドミンの SEO パネルで SEO タイトル、メタ説明、OG 画像、正規 URL、「検索エンジンから非表示」(noindex) トグルを設定できます。そのデータは entry.data.seo としてエントリに含まれますが、テンプレートが出力しない限りレンダリングされたページには反映されません。getSeoMeta を使用して、パネルフィールドをレンダリング可能なメタタグに変換します:

---
import { getEmDashEntry, getSeoMeta } from "emdash";

const { entry, error } = await getEmDashEntry("posts", Astro.params.slug);
if (error) {
  return new Response("サーバーエラー", { status: 500 });
}
if (!entry) return Astro.redirect("/404");

const seo = getSeoMeta(entry, {
  siteTitle: "マイサイト",
  siteUrl: "https://example.com",
  path: Astro.url.pathname,
});
---

<head>
  <title>{seo.title}</title>
  {seo.description && <meta name="description" content={seo.description} />}
  {seo.ogImage && <meta property="og:image" content={seo.ogImage} />}
  {seo.canonical && <link rel="canonical" href={seo.canonical} />}
  {seo.robots && <meta name="robots" content={seo.robots} />}
</head>

プレビューモード

EmDash はミドルウェアを通じてプレビューを自動的に処理します:

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

ビジュアルエディティング

<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 属性を生成します。本番環境では出力を生成しません。

インラインコードブロックのスタイリング

プロパティ用途
--emdash-inline-code-backgroundコードブロックの背景
--emdash-inline-code-foregroundプレーンコードテキスト
--emdash-inline-code-mutedコメントと引用テキスト
--emdash-inline-code-keywordキーワード、リテラル、セレクター、削除テキスト
--emdash-inline-code-string文字列、属性、シンボル、追加テキスト
--emdash-inline-code-number数値とメタデータ
--emdash-inline-code-titleタイトル、名前、型、ビルトイン
--emdash-inline-code-border言語セレクターのボーダー
--emdash-inline-code-control-background言語セレクターの背景
--emdash-inline-code-control-foreground言語セレクターのテキストとアイコン
--emdash-inline-code-focusキーボードフォーカスインジケーター
:root {
  --emdash-inline-code-background: #f7f7f5;
  --emdash-inline-code-foreground: #24292f;
  --emdash-inline-code-muted: #57606a;
  --emdash-inline-code-keyword: #b8172a;
  --emdash-inline-code-string: #0a3069;
  --emdash-inline-code-number: #0550ae;
  --emdash-inline-code-title: #7545c7;
  --emdash-inline-code-border: #7d8590;
  --emdash-inline-code-control-background: #fff;
  --emdash-inline-code-control-foreground: #24292f;
  --emdash-inline-code-focus: #0550ae;
}

:root.dark {
  --emdash-inline-code-background: #202020;
  --emdash-inline-code-foreground: #f0f3f6;
  --emdash-inline-code-muted: #c9d1d9;
  --emdash-inline-code-keyword: #ffc1bb;
  --emdash-inline-code-string: #b9ddff;
  --emdash-inline-code-number: #a8d5ff;
  --emdash-inline-code-title: #e5ccff;
  --emdash-inline-code-border: #6e7681;
  --emdash-inline-code-control-background: #161b22;
  --emdash-inline-code-control-foreground: #f0f3f6;
  --emdash-inline-code-focus: #a8d5ff;
}

結果のソート

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 型

npx emdash types
import { getEmDashCollection, getEmDashEntry } from "emdash";
import type { Post } from "../.emdash/types";

const { entries: posts } = await getEmDashCollection<Post>("posts");
const { entry: post } = await getEmDashEntry<Post>("posts", "my-post");

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

静的(プリレンダリング)

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

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

キャッシング

---
const { entries: posts } = await getEmDashCollection("posts", { status: "published" });
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} />

次のステップ