이 페이지는 Astro 페이지, 레이아웃, 컴포넌트가 EmDash 사이트를 읽고 표시할 때 사용하는 공개 API를 다룹니다. emdash 패키지 루트의 모든 export를 나열하지는 않습니다. 데이터베이스 리포지토리, API 핸들러, 마이그레이션 유틸리티, 플러그인 작성 API 및 기타 서버 내부는 별도 참조에 있거나 프레임워크 통합 코드용입니다.
이 참조의 사이트 템플릿 API 계약은 EmDash가 유지하는 사이트 템플릿이 emdash에서 import하는 모든 함수를 나열합니다. MediaValue 같은 타입 전용 import는 해당 데이터 모델 문서에 속합니다. 관련 함수는 사이트 작성자에게 도움이 될 때 같은 섹션에 문서화하지만, 이 페이지는 관련 없는 루트 export는 다루지 않습니다.
사이트 템플릿 API 계약
유지되는 템플릿은 다음 런타임 헬퍼를 호출합니다.
| 함수 | 용도 |
|---|---|
decodeSlug | 조회 전 동적 라우트 매개변수 디코딩 |
getEmDashCollection | 컬렉션의 항목 읽기 및 필터링 |
getEmDashEntry | ID 또는 slug로 단일 항목 읽기 |
getMenuWithCacheHint | 캐시 무효화와 함께 내비게이션 메뉴 렌더링 |
getSeoMeta | 항목의 SEO 패널 값 및 템플릿 fallback 해석 |
getSiteSettings | 공개 사이트 식별 정보 및 기타 전역 설정 읽기 |
getSiteSettingsWithCacheHint | 캐시 무효화와 함께 전역 설정 읽기 |
getTaxonomyTermsWithCacheHint | 캐시 무효화와 함께 택소노미 필터 렌더링 |
getTermsForEntries | 항목 목록에 대해 하나의 택소노미 일괄 로드 |
sanitizeHref | 저장된 링크를 렌더링하기 전에 안전하지 않은 URL scheme 거부 |
search | 컬렉션 전체에서 게시된 콘텐츠 검색 |
콘텐츠 쿼리
EmDash 쿼리 함수는 Astro의 live content collections 패턴을 따르며, 우아한 오류 처리를 위해 { entries, error } 또는 { entry, error }를 반환합니다.
getEmDashCollection()
컬렉션의 모든 항목을 가져옵니다. 다음 예는 모든 게시물을 로드하고 오류를 확인합니다.
import { getEmDashCollection } from "emdash";
const { entries: posts, error } = await getEmDashCollection("posts");
if (error) {
console.error("Failed to load posts:", error);
}
매개변수
| 매개변수 | 타입 | 설명 |
|---|---|---|
collection | string | 컬렉션 slug |
options | CollectionFilter | 선택적 필터 옵션 |
옵션
options 매개변수는 다음 필터를 받습니다.
interface WhereRange {
gt?: string;
gte?: string;
lt?: string;
lte?: string;
}
interface CollectionFilter {
status?: "draft" | "published" | "archived";
limit?: number;
cursor?: string; // Keyset pagination — pass a previous `nextCursor`
offset?: number; // Offset pagination — skip N entries (use with `limit`)
where?: Record<string, string | string[] | WhereRange>;
orderBy?: Record<string, "asc" | "desc">;
locale?: string;
}
cursor와 offset은 함께 사용할 수 없습니다. where 키는 콘텐츠 필드, 택소노미 또는 byline을 지정할 수 있으며, range 객체는 순서가 있는 비교에 사용할 수 있습니다.
반환값
함수는 CollectionResult로 resolve됩니다.
interface CollectionResult<T> {
entries: ContentEntry<T>[]; // Empty array if error or none found
error?: Error; // Set if query failed
cacheHint: CacheHint; // Tags and last-modified time for Astro route caching
nextCursor?: string; // Cursor for the next keyset page, if any
hasMore?: boolean; // Whether more entries exist beyond this page (when `limit` is set)
}
예제
다음 예는 상태와 택소노미로 필터링하고, 결과를 제한하며, 오류를 처리합니다.
// Get all published posts
const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
});
// Get latest 5 posts
const { entries: latest } = await getEmDashCollection("posts", {
limit: 5,
status: "published",
});
// Filter by taxonomy
const { entries: newsPosts } = await getEmDashCollection("posts", {
status: "published",
where: { category: "news" },
});
// Numbered archive page (e.g. /page/3) with offset pagination
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" },
});
// Handle errors
const { entries, error } = await getEmDashCollection("posts");
if (error) {
return new Response("Server error", { status: 500 });
}
getEmDashEntry()
slug 또는 ID로 단일 항목을 가져옵니다. 다음 예는 게시물을 로드하고 없으면 리다이렉트합니다.
import { getEmDashEntry } from "emdash";
const { entry: post, error } = await getEmDashEntry("posts", "my-post-slug");
if (!post) {
return Astro.redirect("/404");
}
매개변수
| 매개변수 | 타입 | 설명 |
|---|---|---|
collection | string | 컬렉션 slug |
slugOrId | string | 항목 slug 또는 ID |
options | { locale?: string; references?: ReferenceSelection } | 선택 사항. slug 해석용 locale 및 로드할 reference 필드 |
미리보기 모드는 자동으로 처리됩니다. 요청에 유효한 _preview 토큰이 있으면 쿼리가 초안 콘텐츠를 제공합니다. 미리보기 상태에는 매개변수가 필요하지 않습니다.
references는 필드 slug를 키로 하여 로드할 reference 필드를 지정합니다. true는 기본 limit 50개 항목의 첫 페이지를 요청합니다. 객체 형태는 limit(최대 100)과 이전 페이지의 cursor를 받습니다. 생략된 필드는 읽지 않으며, 옵션을 생략하면 reference 쿼리는 실행되지 않습니다.
반환값
함수는 EntryResult로 resolve됩니다.
interface EntryResult<T, R> {
entry: ContentEntry<T, R> | null; // null if not found
error?: Error; // Set only for actual errors, not "not found"
isPreview: boolean; // true if draft content is being served
fallbackLocale?: string; // Set when locale fallback returned another locale
cacheHint: CacheHint; // Tags and last-modified time for Astro route caching
}
예제
다음 예는 slug와 ID로 가져오고, 미리보기 상태를 읽으며, 오류와 not-found를 구분합니다.
// Get by slug
const { entry: post } = await getEmDashEntry("posts", "hello-world");
// Get by ID
const { entry: post } = await getEmDashEntry("posts", "01HXK5MZSN0FVXT2Q3KPRT9M7D");
// Preview is automatic — isPreview is true when a valid _preview token is present
const { entry, isPreview, error } = await getEmDashEntry("posts", slug);
// Handle errors vs not-found
if (error) {
return new Response("Server error", { status: 500 });
}
if (!entry) {
return Astro.redirect("/404");
}
getTranslations()
컬렉션 slug와 데이터베이스 ID로 한 항목의 사용 가능한 번역을 가져옵니다.
import { getTranslations } from "emdash";
const { translations, error } = await getTranslations("posts", post.data.id);
결과에는 공유 translationGroup, translations 배열, 선택적 error가 포함됩니다. 각 번역 요약에는 id, locale, slug, status가 있습니다.
resolveEmDashPath()
라우팅 가능한 컬렉션에 구성된 URL 패턴에 대해 공개 pathname을 해석합니다.
import { resolveEmDashPath } from "emdash";
const result = await resolveEmDashPath("/blog/hello-world");
if (result) {
console.log(result.collection, result.entry.data.title);
}
결과에는 일치한 collection, entry, 라우트 params가 포함됩니다. 구성된 URL 패턴과 일치하지 않으면 함수는 null을 반환합니다.
getEditMeta()
Portable Text 값에 붙은 non-enumerable 시각 편집 메타데이터를 읽습니다.
import { getEditMeta } from "emdash";
const meta = getEditMeta(post.data.content);
주석이 있는 값에 대해서는 { collection, id, field }를 반환하고, 주석이 없으면 undefined를 반환합니다.
콘텐츠 타입
ContentEntry
쿼리 함수는 다음 형태의 항목을 반환합니다.
interface ContentEntry<T = Record<string, unknown>, R = ReferencePages> {
id: string;
data: T;
references?: R; // One page per reference field requested; absent otherwise
edit: EditProxy; // Visual editing annotations
}
edit 프록시는 시각 편집 주석을 제공합니다. 요소에 spread하여 인라인 편집을 활성화합니다: {...entry.edit.title}. 편집 모드가 아니면 출력이 없습니다.
data 객체에는 모든 콘텐츠 필드와 시스템 필드가 포함됩니다.
id- 고유 식별자slug- URL 친화적 식별자status- “draft” | “published” | “archived”createdAt-Date로 표현된 생성 시각updatedAt-Date로 표현된 마지막 수정 시각publishedAt-Date로 표현된 게시 시각, 또는null. 콘텐츠가 게시 취소되어도 유지됩니다- 컬렉션 스키마에 정의한 모든 사용자 정의 필드
ReferencePage
entry.references는 getEmDashEntry가 요청한 reference 필드마다 필드 slug를 키로 한 페이지를 하나씩 보관합니다.
interface ReferencePage<T = Record<string, unknown>> {
entries: ContentEntry<T>[];
nextCursor?: string; // Set when the field holds more entries than the limit
}
각 항목은 직접 로드한 항목과 동일하게 매핑되며, 한 가지 예외가 있습니다. 바이라인과 택소노미 용어는 hydrate되지 않으므로 data.bylines와 data.terms는 없습니다.
getEmDashReferences()
항목 전체를 다시 읽지 않고 단일 reference 필드의 한 페이지를 가져옵니다. 해당 페이지가 반환한 nextCursor로 첫 페이지 이후를 순회할 때 사용합니다.
| 매개변수 | 타입 | 설명 |
|---|---|---|
collection | string | 필드를 보유한 항목의 컬렉션 slug |
slugOrId | string | 항목 slug 또는 ID |
field | string | reference 필드 slug |
options | { limit?: number; cursor?: string; locale?: string } | 선택 사항. 기본값 50개 항목, 최대 100 |
다음 예는 연결된 다음 20개 항목을 로드합니다.
import { getEmDashReferences } from "emdash";
const { entries, nextCursor, error } = await getEmDashReferences(
"posts",
post.id,
"related_posts",
{ cursor, limit: 20 },
);
error는 실제 오류일 때만 설정됩니다. 알 수 없는 필드, 없는 항목, 요청에서 볼 수 없는 항목은 모두 빈 entries 배열로 resolve됩니다.
결과에는 해당 페이지가 읽은 행을 가리키는 cacheHint도 포함됩니다. 렌더링 결과를 캐시하는 라우트는 getEmDashEntry({ references })가 첫 페이지 hint를 병합하는 것처럼 자체 hint에 merge해야, 해당 항목 중 하나에 쓰기가 발생할 때 페이지 캐시가 만료됩니다.
URL 헬퍼
decodeSlug() and slugify()
콘텐츠 쿼리에 넘기기 전에 동적 라우트 매개변수에 decodeSlug()를 사용합니다. 매개변수가 없으면 undefined를 반환하고, 그렇지 않으면 decodeURIComponent()를 적용하며, 잘못된 퍼센트 인코딩에 대해서는 예외를 던집니다.
function decodeSlug(raw: string | undefined): string | undefined;
import { decodeSlug, getEmDashEntry } from "emdash";
const slug = decodeSlug(Astro.params.slug);
const { entry } = slug ? await getEmDashEntry("posts", slug) : { entry: null };
slugify(value)는 텍스트를 소문자 하이픈 구분 slug로 변환합니다. 템플릿이 레이블에서 slug를 만들어야 할 때 사용합니다. 저장된 항목 slug는 이미 EmDash에서 제공됩니다.
sanitizeHref() and isSafeHref()
이 헬퍼는 javascript: 같은 안전하지 않은 링크 scheme을 거부합니다. isSafeHref(value)는 boolean을 반환합니다. sanitizeHref(value)는 안전한 원본 URL을 반환하거나, 값이 비어 있거나 안전하지 않으면 "#"를 반환합니다.
import { sanitizeHref } from "emdash";
const href = sanitizeHref(menuItem.url);
미리보기 시스템
generatePreviewToken()
초안 콘텐츠용 미리보기 토큰을 생성합니다. 다음 예는 1시간 후 만료되는 토큰을 만듭니다.
import { generatePreviewToken } from "emdash";
const token = await generatePreviewToken({
contentId: "posts:01HXK5MZSN...",
secret: process.env.EMDASH_PREVIEW_SECRET!,
expiresIn: 3600, // 1 hour
});
contentId는 collection:id 형식이어야 합니다. expiresIn은 초 또는 s, m, h, d, w로 끝나는 기간을 받으며 기본값은 "1h"입니다. 서명 secret은 서버에만 두세요.
verifyPreviewToken()
미리보기 토큰을 검증하고 payload를 읽습니다.
import { verifyPreviewToken } from "emdash";
const result = await verifyPreviewToken({
token,
secret: process.env.EMDASH_PREVIEW_SECRET!,
});
if (result.valid) {
const { cid, exp, iat } = result.payload;
// cid is "collection:id" format, e.g. "posts:my-draft-post"
}
서명 secret과 함께 token 또는 url을 전달합니다. 유효하지 않은 토큰은 { valid: false, error }를 반환하며, error는 "none", "malformed", "invalid", "expired" 중 하나입니다.
parseContentId()
미리보기 payload의 collection:id 값을 두 부분으로 나눕니다.
import { parseContentId } from "emdash";
const parsed = parseContentId(result.payload.cid);
{ collection, id }를 반환하며, 값에 콜론 구분자가 없으면 예외를 던집니다.
getPreviewUrl() and buildPreviewUrl()
getPreviewUrl()은 미리보기 URL을 만들고 서명합니다. collection, id, secret과 선택적 expiresIn, baseUrl, pathPattern, locale을 받습니다.
import { getPreviewUrl } from "emdash";
const previewUrl = await getPreviewUrl({
collection: "posts",
id: post.id,
secret: process.env.EMDASH_PREVIEW_SECRET!,
pathPattern: "/blog/{id}",
});
baseUrl 없이는 사이트 상대 URL을 반환합니다. 토큰이 이미 있을 때는 buildPreviewUrl({ path, token, baseUrl? })를 사용합니다.
isPreviewRequest()
요청에 미리보기 토큰이 포함되어 있는지 확인한 뒤 읽습니다.
import { isPreviewRequest, getPreviewToken } from "emdash";
if (isPreviewRequest(Astro.url)) {
const token = getPreviewToken(Astro.url);
// Verify and show preview content
}
getPreviewToken()은 _preview 쿼리 매개변수를 반환하거나 없으면 null을 반환합니다. EmDash 미들웨어는 일반 미리보기 요청을 검증하고 getEmDashEntry()에 미리보기 상태를 자동으로 제공합니다. 이 헬퍼는 사용자 정의 미리보기 라우트와 도구용입니다.
콘텐츠 변환기
Portable Text와 ProseMirror 형식 간 변환:
import { prosemirrorToPortableText, portableTextToProsemirror } from "emdash";
// From ProseMirror (editor) to Portable Text (storage)
const portableText = prosemirrorToPortableText(prosemirrorDoc);
// From Portable Text to ProseMirror
const prosemirrorDoc = portableTextToProsemirror(portableText);
사이트 설정
getSiteSettings와 getSiteSetting으로 사이트 전역 설정을 읽습니다.
function getSiteSettings(): Promise<Partial<SiteSettings>>;
function getSiteSetting<K extends SiteSettingKey>(key: K): Promise<SiteSettings[K] | undefined>;
import { getSiteSettings, getSiteSetting } from "emdash";
// Get all settings
const settings = await getSiteSettings();
// Get single setting
const title = await getSiteSetting("title");
런타임 API에서는 설정이 읽기 전용입니다. 업데이트는 admin API를 사용하세요.
getSiteSettings()는 설정되지 않은 키가 생략되므로 partial 객체를 반환합니다. 사이트 로고, favicon 같은 미디어 설정은 함수가 반환하기 전에 미디어 reference 객체로 resolve됩니다.
getSiteSettingsWithCacheHint()는 { data, cacheHint }를 반환합니다. Astro 라우트 캐시가 사이트 설정 변경 후 무효화되어야 하면 hint를 Astro.cache.set()에 전달하세요.
SEO
getSeoMeta()로 페이지의 SEO 패널 값과 콘텐츠 fallback을 해석합니다.
function getSeoMeta<T>(content: SeoContentInput<T>, options?: SeoMetaOptions): SeoMeta;
interface SeoMetaOptions {
siteTitle?: string;
siteUrl?: string;
titleSeparator?: string; // Default: " | "
path?: string;
defaultOgImage?: string;
defaultTitle?: string;
defaultDescription?: string;
}
interface SeoMeta {
title: string;
description: string | null;
ogTitle: string;
ogDescription: string | null;
ogImage: string | null;
canonical: string | null;
robots: string | null;
}
import { getSeoMeta } from "emdash";
const meta = getSeoMeta(post, {
siteTitle: "Example Blog",
siteUrl: "https://example.com",
path: `/blog/${post.data.slug}`,
});
해석된 title, description, Open Graph 값, canonical URL, robots 값을 반환합니다. getContentSeo(content)는 템플릿 fallback을 적용하지 않고 raw SEO 객체를 반환합니다.
번역된 콘텐츠의 경우 getHreflangAlternates(collection, entryId, { siteUrl? })는 게시되고 라우팅 가능한 locale 변형을 { hreflang, href } 객체로 반환하고 x-default 항목을 추가합니다. 국제화가 비활성화되었거나, 현재 항목이 noindex로 표시되었거나, 절대 사이트 URL을 사용할 수 없으면 빈 배열을 반환합니다.
댓글
항목에 대한 승인된 댓글과 개수를 가져옵니다.
import { getCommentCount, getComments } from "emdash";
const { items: comments, total } = await getComments({
collection: "posts",
contentId: post.data.id,
threaded: true,
reactions: true,
sort: "best",
});
const count = await getCommentCount("posts", post.data.id);
threaded는 답글을 부모 아래에 중첩합니다. 기본 sort는 "oldest"이며, "best"는 최상위 댓글을 reaction 순으로 정렬하고 reaction 개수를 자동으로 활성화합니다. 서버 렌더링 댓글 쿼리는 승인된 댓글을 최대 500개까지 반환합니다. 클라이언트에 페이지네이션이 필요하면 REST API를 사용하세요.
메뉴
내비게이션 메뉴를 가져와 중첩된 자식을 포함한 항목을 순회합니다.
function getMenu(name: string, options?: { locale?: string }): Promise<Menu | null>;
function getMenus(options?: { locale?: string }): Promise<MenuSummary[]>;
import { getMenu, getMenus } from "emdash";
// Get all menus
const menus = await getMenus();
// Get specific menu with items
const primaryMenu = await getMenu("primary");
if (primaryMenu) {
primaryMenu.items.forEach(item => {
console.log(item.label, item.url);
// Nested items for dropdowns
item.children.forEach(child => console.log(" -", child.label));
});
}
getMenu(name, { locale? })는 구성된 locale fallback 체인을 따릅니다. getMenus({ locale? })는 resolve된 요청 또는 구성 locale의 메뉴 요약을 나열합니다. 국제화가 없으면 모든 locale을 나열합니다. getMenuWithCacheHint()는 Astro 캐시를 사용하는 라우트에 { data, cacheHint }를 반환합니다.
바이라인
ID 또는 slug로 작성자 프로필을 가져오거나, 바이라인에 기인한 항목을 나열합니다.
import { getByline, getBylineBySlug, getEntriesByByline } from "emdash";
const profile = await getBylineBySlug("jane-doe", { locale: "en" });
const posts = profile
? await getEntriesByByline("posts", profile.translationGroup ?? profile.id)
: [];
getByline(id)는 프로필 하나 또는 null을 반환합니다. slug 조회는 선택적 locale을 받고 locale fallback 체인을 따릅니다. 콘텐츠 쿼리는 이미 항목의 정렬된 credit을 entry.data.bylines에 hydrate합니다. 작성자 페이지와 바이라인 아카이브에는 이 독립 헬퍼를 사용하세요.
택소노미
택소노미 용어, 단일 용어, 항목의 용어, 용어별 항목을 가져옵니다.
function getTaxonomyTerms(
taxonomyName: string,
options?: { locale?: string; includeCounts?: boolean },
): Promise<TaxonomyTerm[]>;
function getTerm(
taxonomyName: string,
slug: string,
options?: { locale?: string; includeCounts?: boolean },
): Promise<TaxonomyTerm | null>;
function getTermsForEntries(
collection: string,
entryIds: string[],
taxonomyName: string,
options?: { locale?: string },
): Promise<Map<string, TaxonomyTerm[]>>;
import { getTaxonomyTerms, getTerm, getEntryTerms, getEntriesByTerm } from "emdash";
// Get all terms for a taxonomy (tree structure for hierarchical)
const categories = await getTaxonomyTerms("category");
// Get single term
const news = await getTerm("category", "news");
// Get terms assigned to a content entry
const postCategories = await getEntryTerms("posts", "post-123", "category");
// Get entries with a specific term
const newsPosts = await getEntriesByTerm("posts", "category", "news");
getTaxonomyDefs({ locale? })는 택소노미당 정의 하나를 나열하고, getTaxonomyDef(name, { locale? })는 정의 하나를 반환하며 locale이 택소노미를 정의하지 않으면 null입니다. 둘 다 모든 locale에서 택소노미를 resolve합니다. 어떤 locale의 레이블을 사용하는지는 택소노미와 용어 번역을 참고하세요. 용어 조회는 구성된 fallback 체인을 따릅니다.
getTaxonomyTerms(name, { locale?, includeCounts? })는 계층형 택소노미에 대해 트리를 반환하며 기본적으로 표시 가능한 항목 개수를 포함합니다. 템플릿이 개수를 렌더링하지 않으면 includeCounts: false를 전달하세요. 콘텐츠 쿼리는 할당된 용어를 각 항목의 data.terms에 hydrate합니다. 컬렉션 이름과 항목 ID만 있을 때는 getEntryTerms()를 사용하세요.
많은 항목 옆에 용어를 렌더링하는 아카이브 페이지에서는 루프에서 getEntryTerms()를 반복 호출하지 말고 일괄 조회하세요.
import { getAllTermsForEntries, getTermsForEntries } from "emdash";
const termsByPost = await getTermsForEntries(
"posts",
posts.map(post => post.data.id),
"category",
);
const allTermsByPost = await getAllTermsForEntries(
"posts",
posts.map(post => post.data.id),
);
getTermsForEntries()는 항목 ID에서 요청한 택소노미의 용어로의 Map을 반환합니다. getAllTermsForEntries()는 항목 ID에서 택소노미 이름별로 그룹화된 용어로의 Map을 반환합니다. getTaxonomyTermsWithCacheHint()는 Astro 캐시 라우트에 { data, cacheHint }를 반환합니다.
위젯 영역
위젯 영역과 포함된 위젯을 가져옵니다.
import { getWidgetArea, getWidgetAreas } from "emdash";
// Get all widget areas
const areas = await getWidgetAreas();
// Get specific widget area with widgets
const sidebar = await getWidgetArea("sidebar");
if (sidebar) {
sidebar.widgets.forEach(widget => {
console.log(widget.type, widget.title);
});
}
getWidgetAreaWithCacheHint(name)은 Astro 캐시를 사용하는 라우트에 { data, cacheHint }를 반환합니다. 위젯 영역과 위젯은 구성된 정렬 순서대로 정렬됩니다.
섹션
섹션을 가져와 필터링합니다.
import { getSection, getSections } from "emdash";
// Get all sections (paginated)
const { items, nextCursor } = await getSections();
// Filter sections
const { items: themeSections } = await getSections({ source: "theme" });
const { items: results } = await getSections({ search: "newsletter" });
// Get a single section by slug
const cta = await getSection("newsletter-cta");
getSections(options?)는 { items: Section[]; nextCursor?: string }를 반환합니다. 옵션은 source("theme" | "user" | "import"), search, limit(기본 50, 최대 100), cursor입니다.
검색
컬렉션 전체에서 전역 검색을 실행합니다. 결과에는 하이라이트된 snippet이 포함됩니다.
function search(query: string, options?: SearchOptions): Promise<SearchResponse>;
interface SearchOptions {
collections?: string[]; // Default: every searchable collection
status?: string; // Default: "published"
locale?: string; // Default: all locales
limit?: number; // Default: 20
cursor?: string;
scope?: "all" | "title"; // Default: "all"
}
interface SearchResponse {
items: SearchResult[];
nextCursor?: string;
}
import { search } from "emdash";
const results = await search("hello world", {
collections: ["posts", "pages"],
status: "published",
limit: 20,
});
// search() resolves to { items, nextCursor? }
results.items.forEach(result => {
console.log(result.title);
console.log(result.snippet); // Contains <mark> tags
console.log(result.score);
});
// Paginate: pass the previous nextCursor back as `cursor` to get the next page.
// nextCursor is undefined once there are no more results.
if (results.nextCursor) {
const next = await search("hello world", {
collections: ["posts", "pages"],
limit: 20,
cursor: results.nextCursor,
});
}
scope: "title"이면 쿼리는 전체 인덱스 텍스트가 아니라 각 컬렉션의 title 필드만 일치시킵니다. 본문 일치가 예상 밖인 picker와 autocomplete에 유용합니다. title 필드가 검색용으로 인덱스되지 않은 컬렉션은 이 scope에서 결과가 없습니다.
오류 처리
콘텐츠 쿼리는 throw 대신 결과에 운영 오류를 담아 반환합니다. 없는 항목은 운영 오류가 아닙니다. entry는 null이고 error는 undefined로 남습니다.
const { entry, error } = await getEmDashEntry("posts", slug);
if (error) {
return new Response("Content could not be loaded", { status: 500 });
}
if (!entry) {
return Astro.redirect("/404");
}
결과 envelope이 없는 헬퍼는 입력이 유효하지 않거나 데이터베이스 작업이 실패할 때 throw할 수 있습니다. 페이지가 유용한 fallback을 제공할 수 있을 때는 라우트 경계에서 이러한 오류를 처리하세요. 하위 수준 서버 통합에서 사용하는 리포지토리 및 핸들러 오류 클래스는 이 사이트 템플릿 API 범위 밖입니다.