JavaScript APIリファレンス

このページ

EmDashは、コンテンツのクエリ、プレビュー、設定、メニュー、タクソノミー、ウィジェットエリア、セクション、検索を扱うための関数をエクスポートします。

コンテンツクエリ

EmDashのクエリ関数はAstroのライブコンテンツコレクションパターンに従い、エレガントなエラーハンドリングのために { entries, error } または { entry, error } を返します。

getEmDashCollection()

コレクションのすべてのエントリを取得します。以下の例はすべての投稿を読み込み、エラーをチェックします:

import { getEmDashCollection } from "emdash";

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

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

パラメータ

パラメータ説明
collectionstringコレクションスラッグ
optionsCollectionFilterオプションのフィルタオプション

オプション

options パラメータは以下のフィルタを受け付けます:

interface CollectionFilter {
	status?: "draft" | "published" | "archived";
	limit?: number;
	cursor?: string; // キーセットページネーション — 前の `nextCursor` を渡す
	offset?: number; // オフセットページネーション — N件スキップ(`limit` と併用)
	where?: Record<string, string | string[]>; // フィールドまたはタクソノミーでフィルタ
}

戻り値

関数は CollectionResult に解決されます:

interface CollectionResult<T> {
	entries: ContentEntry<T>[]; // エラーまたは見つからない場合は空配列
	error?: Error; // クエリが失敗した場合に設定
	nextCursor?: string; // 次のキーセットページのカーソル(存在する場合)
	hasMore?: boolean; // このページを超えてエントリが存在するかどうか(`limit` 設定時)
}

以下の例はステータスとタクソノミーでフィルタし、結果を制限し、エラーを処理します:

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

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

// タクソノミーでフィルタ
const { entries: newsPosts } = await getEmDashCollection("posts", {
	status: "published",
	where: { category: "news" },
});

// 番号付きアーカイブページ(例:/page/3)のオフセットページネーション
const perPage = 20;
const page = Number(Astro.params.page ?? 1);
const { entries: pagePosts, hasMore } = await getEmDashCollection("posts", {
	status: "published",
	limit: perPage,
	offset: (page - 1) * perPage,
	orderBy: { published_at: "desc" },
});

// エラー処理
const { entries, error } = await getEmDashCollection("posts");
if (error) {
	return new Response("サーバーエラー", { status: 500 });
}

getEmDashEntry()

スラッグまたはIDで単一のエントリを取得します。以下の例は投稿を読み込み、見つからない場合にリダイレクトします:

import { getEmDashEntry } from "emdash";

const { entry: post, error } = await getEmDashEntry("posts", "my-post-slug");

if (!post) {
	return Astro.redirect("/404");
}

パラメータ

パラメータ説明
collectionstringコレクションスラッグ
slugOrIdstringエントリのスラッグまたはID
options{ locale?: string }オプション。スラッグ解決用のロケール

プレビューモードは自動的に処理されます — ミドルウェアが _preview トークンを検出し、AsyncLocalStorage を通じてドラフトコンテンツを提供します。オプションの options パラメータはスラッグ解決用の locale のみを受け付けます。プレビュー状態にパラメータは不要です。

戻り値

関数は EntryResult に解決されます:

interface EntryResult<T> {
	entry: ContentEntry<T> | null; // 見つからない場合はnull
	error?: Error; // 実際のエラーの場合のみ設定、「見つからない」では設定されない
	isPreview: boolean; // ドラフトコンテンツが提供されている場合true
}

以下の例はスラッグとIDで取得し、プレビュー状態を読み取り、エラーと見つからないを区別します:

// スラッグで取得
const { entry: post } = await getEmDashEntry("posts", "hello-world");

// IDで取得
const { entry: post } = await getEmDashEntry("posts", "01HXK5MZSN0FVXT2Q3KPRT9M7D");

// プレビューは自動 — 有効な_previewトークンがある場合isPreviewはtrue
const { entry, isPreview, error } = await getEmDashEntry("posts", slug);

// エラー vs 見つからないの処理
if (error) {
	return new Response("サーバーエラー", { status: 500 });
}
if (!entry) {
	return Astro.redirect("/404");
}

コンテンツタイプ

ContentEntry

クエリ関数は以下の形式でエントリを返します:

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

edit プロキシはビジュアル編集アノテーションを提供します。要素にスプレッドしてインライン編集を有効にします:{...entry.edit.title}。本番環境では出力を生成しません。

data オブジェクトにはすべてのコンテンツフィールドとシステムフィールドが含まれます:

  • id - 一意の識別子
  • slug - URL フレンドリーな識別子
  • status - “draft” | “published” | “archived”
  • createdAt - ISOタイムスタンプ
  • updatedAt - ISOタイムスタンプ
  • publishedAt - ISOタイムスタンプまたはnull
  • コレクションスキーマで定義されたすべてのカスタムフィールド

プレビューシステム

generatePreviewToken()

ドラフトコンテンツ用のプレビュートークンを生成します。以下の例は1時間で期限切れになるトークンを作成します:

import { generatePreviewToken } from "emdash";

const token = await generatePreviewToken({
	contentId: "posts:01HXK5MZSN...",
	secret: process.env.EMDASH_ADMIN_SECRET,
	expiresIn: 3600, // 1時間
});

verifyPreviewToken()

プレビュートークンを検証し、そのペイロードを読み取ります:

import { verifyPreviewToken } from "emdash";

const result = await verifyPreviewToken({
	token,
	secret: process.env.EMDASH_ADMIN_SECRET,
});

if (result.valid) {
	const { cid, exp, iat } = result.payload;
	// cidは"collection:id"形式、例:"posts:my-draft-post"
}

isPreviewRequest()

リクエストにプレビュートークンが含まれているか確認し、読み取ります:

import { isPreviewRequest, getPreviewToken } from "emdash";

if (isPreviewRequest(Astro.url)) {
	const token = getPreviewToken(Astro.url);
	// 検証してプレビューコンテンツを表示
}

コンテンツコンバーター

Portable TextとProseMirror形式間の変換:

import { prosemirrorToPortableText, portableTextToProsemirror } from "emdash";

// ProseMirror(エディター)からPortable Text(ストレージ)へ
const portableText = prosemirrorToPortableText(prosemirrorDoc);

// Portable TextからProseMirrorへ
const prosemirrorDoc = portableTextToProsemirror(portableText);

サイト設定

getSiteSettingsgetSiteSetting でサイト全体の設定を読み取ります:

import { getSiteSettings, getSiteSetting } from "emdash";

// すべての設定を取得
const settings = await getSiteSettings();

// 個別の設定を取得
const title = await getSiteSetting("title");

設定はランタイムAPIからは読み取り専用です。更新するにはAdmin APIを使用してください。

メニュー

ナビゲーションメニューを取得し、ネストされた子を含むアイテムを反復処理します:

import { getMenu, getMenus } from "emdash";

// すべてのメニューを取得
const menus = await getMenus();

// 特定のメニューをアイテム付きで取得
const primaryMenu = await getMenu("primary");

if (primaryMenu) {
	primaryMenu.items.forEach(item => {
		console.log(item.label, item.url);
		// ドロップダウン用のネストされたアイテム
		item.children.forEach(child => console.log("  -", child.label));
	});
}

タクソノミー

タクソノミー用語、単一用語、エントリの用語、または用語によるエントリを取得します:

import { getTaxonomyTerms, getTerm, getEntryTerms, getEntriesByTerm } from "emdash";

// タクソノミーのすべての用語を取得(階層型はツリー構造)
const categories = await getTaxonomyTerms("category");

// 単一の用語を取得
const news = await getTerm("category", "news");

// コンテンツエントリに割り当てられた用語を取得
const postCategories = await getEntryTerms("posts", "post-123", "category");

// 特定の用語を持つエントリを取得
const newsPosts = await getEntriesByTerm("posts", "category", "news");

ウィジェットエリア

ウィジェットエリアとそれに含まれるウィジェットを取得します:

import { getWidgetArea, getWidgetAreas } from "emdash";

// すべてのウィジェットエリアを取得
const areas = await getWidgetAreas();

// 特定のウィジェットエリアをウィジェット付きで取得
const sidebar = await getWidgetArea("sidebar");

if (sidebar) {
	sidebar.widgets.forEach(widget => {
		console.log(widget.type, widget.title);
	});
}

セクション

セクションを取得してフィルタします:

import { getSection, getSections } from "emdash";

// すべてのセクションを取得(ページネーション付き)
const { items, nextCursor } = await getSections();

// セクションをフィルタ
const { items: themeSections } = await getSections({ source: "theme" });
const { items: results } = await getSections({ search: "newsletter" });

// スラッグで単一のセクションを取得
const cta = await getSection("newsletter-cta");

getSections(options?){ items: Section[]; nextCursor?: string } を返します。オプションは source"theme" | "user" | "import")、searchlimit(デフォルト50、最大100)、cursor です。

検索

コレクション全体でグローバル検索を実行します。結果にはハイライトされたスニペットが含まれます:

import { search } from "emdash";

const results = await search("hello world", {
	collections: ["posts", "pages"],
	status: "published",
	limit: 20,
});

// search()は { items, nextCursor? } に解決される
results.items.forEach(result => {
	console.log(result.title);
	console.log(result.snippet); // <mark>タグを含む
	console.log(result.score);
});

// ページネーション:次のページを取得するために前のnextCursorを`cursor`として渡す
// 結果がなくなるとnextCursorはundefinedになる
if (results.nextCursor) {
	const next = await search("hello world", {
		collections: ["posts", "pages"],
		limit: 20,
		cursor: results.nextCursor,
	});
}

エラーハンドリング

EmDashは特定のエラーを処理するためのエラークラスをエクスポートします。以下の例はバリデーションとスキーマエラーをキャッチします:

import {
  EmDashDatabaseError,
  EmDashValidationError,
  EmDashStorageError,
  SchemaError,
} from "emdash";

try {
  await repo.create({ ... });
} catch (error) {
  if (error instanceof EmDashValidationError) {
    console.error("バリデーション失敗:", error.message);
  }
  if (error instanceof SchemaError) {
    console.error("スキーマエラー:", error.code, error.details);
  }
}