Skip to content

Feed core — API reference

The complete public surface of @ghostwritr/feed. All reads are keyless: the siteId is the only key. For task-level guidance, start at the overview.

import {
fetchArticles,
fetchArticle,
toArticle,
signFeedPayload,
verifyFeedSignature,
FEED_SIGNATURE_HEADER,
DEFAULT_STATIC_BASE_URL,
GhostwritrError,
} from "@ghostwritr/feed";
import type {
FeedFetchOptions,
Article,
ArticleImage,
Author,
ArticleOrder,
GhostwritrErrorCode,
FeedUpdatedPayload,
} from "@ghostwritr/feed";
fetchArticles(opts: FeedFetchOptions): Promise<Article[]>

Every published article, newest first; de-duped by id. Returns [] for an empty feed; throws NOT_FOUND when the manifest is absent. See Fetchers.

fetchArticle(opts: FeedFetchOptions & { slug: string }): Promise<Article | null>

One article by slug via its by-slug object, or null on a 404. See Fetchers.

toArticle(raw: unknown): Article

Validate and map a single raw feed item. Throws INVALID_RESPONSE on a missing/mistyped guaranteed field or an unparseable timestamp. See Fetchers.

signFeedPayload(secret: string, rawBody: string): Promise<string>

Hex HMAC-SHA256 of the raw body — the value for the X-GW-Signature header. See Webhook helpers.

verifyFeedSignature(
secret: string,
rawBody: string,
signature: string | null | undefined,
): Promise<boolean>

Constant-time verify of a signature against the raw body. false for a missing/empty signature. See Webhook helpers.

const FEED_SIGNATURE_HEADER = "X-GW-Signature";

The header carrying the hex HMAC-SHA256 of the raw webhook body.

const DEFAULT_STATIC_BASE_URL = "https://feeds.ghostwritr.io";

The default static-feed origin. Override per-call with opts.staticBaseUrl.

interface FeedFetchOptions {
siteId: string; // the unguessable read key
staticBaseUrl?: string; // default DEFAULT_STATIC_BASE_URL
fetch?: typeof fetch; // caching / retry / test seam; default global fetch
}
interface Article {
id: string;
title: string; // your <h1>
slug: string; // your route param
markdown: string; // the body (Markdown, not MDX)
seoTitle: string; // falls back to title
seoDescription: string | null;
canonicalUrl: string | null;
formatType: string; // schema.org type hint, e.g. "Article"
inLanguage: string; // BCP-47, e.g. "en"
tags: string[]; // possibly empty
image: ArticleImage | null;
author: Author; // always present
publishedAt: string; // ISO-8601
createdAt: string; // ISO-8601
updatedAt: string; // ISO-8601
}

A published article is a complete article: markdown, seoTitle, publishedAt, and author are guaranteed (with server-side fallbacks). See the article shape.

interface ArticleImage {
url: string; // ready-to-use responsive URL (default/large width)
alt: string; // may be empty for a decorative cover
width: number | null; // set <img width/height> to prevent layout shift
height: number | null;
srcset: string | null; // for <img srcset>
}
interface Author {
name: string;
slug: string;
bio: string | null;
avatarUrl: string | null;
jobTitle: string | null;
sameAs: string[]; // profile/social URLs
kind: "real" | "persona"; // "real" → Person JSON-LD; "persona" → Organization
}

author.kind drives JSON-LD: "real" emits a schema.org Person, "persona" emits an Organization. See SEO & JSON-LD.

type ArticleOrder = "published-desc" | "feed";

Ordering hint for an adapter’s article list. "published-desc" (default) is newest-first; "feed" is the static feed’s native order (also newest-first by updatedAt).

type GhostwritrErrorCode =
| "CONFIG"
| "NETWORK_ERROR"
| "UNAUTHORIZED"
| "FORBIDDEN"
| "NOT_FOUND"
| "RATE_LIMITED"
| "SERVER_ERROR"
| "INVALID_RESPONSE"
| (string & {}); // open to future codes

See Error handling.

interface FeedUpdatedPayload {
event: "feed.updated";
siteId: string;
buildId: string;
}

The webhook body POSTed when a site’s feed rebuilds. See Webhook helpers.

class GhostwritrError extends Error {
readonly status: number; // HTTP status, or 0 for client-side errors
readonly code: GhostwritrErrorCode | null;
readonly details?: unknown;
}

The single error type thrown by every function. See Error handling.