Skip to content

Data fetching

fetchArticles and fetchArticle are the keyless feed fetchers, re-exported from the shared @ghostwritr/feed core. They read your published articles straight from the static feed at feeds.ghostwritr.io/{siteId}/. Call them inside a SvelteKit server load so the site id stays off the client and the feed read happens on the server.

import { fetchArticles, fetchArticle } from "@ghostwritr/svelte";
// every published article, newest first
const articles = await fetchArticles({ siteId });
// one by slug — or null when it isn't in the feed
const article = await fetchArticle({ siteId, slug });

The common case. A +page.server.ts load runs the fetcher on the server during SSR (and prerender), then hands the result to the page as data. Read siteId from $env/dynamic/private so it never reaches the client bundle:

src/routes/blog/+page.server.ts
import { fetchArticles } from "@ghostwritr/svelte";
import { env } from "$env/dynamic/private";
export async function load() {
return { articles: await fetchArticles({ siteId: env.GHOSTWRITR_SITE_ID }) };
}

For a single article, read the slug from params and turn a null result into a real 404 with SvelteKit’s error helper:

src/routes/blog/[slug]/+page.server.ts
import { error } from "@sveltejs/kit";
import { fetchArticle } from "@ghostwritr/svelte";
import { env } from "$env/dynamic/private";
export async function load({ params }) {
const article = await fetchArticle({
siteId: env.GHOSTWRITR_SITE_ID,
slug: params.slug,
});
if (!article) throw error(404, "Not found");
return { article };
}

The siteId is a read capability, not a secret — but keeping it in a private env var keeps it out of the client bundle as a matter of habit:

ContextSource
Server load (+page.server.ts)$env/dynamic/privateenv.GHOSTWRITR_SITE_ID
Static (known at build)$env/static/privateGHOSTWRITR_SITE_ID
Build / prerender scriptprocess.env.GHOSTWRITR_SITE_ID

Use $env/static/private when the value is fixed at build time (it’s inlined and tree-shakeable); use $env/dynamic/private when it’s resolved at runtime:

src/routes/blog/+page.server.ts
import { fetchArticles } from "@ghostwritr/svelte";
import { GHOSTWRITR_SITE_ID } from "$env/static/private";
export async function load() {
return { articles: await fetchArticles({ siteId: GHOSTWRITR_SITE_ID }) };
}

The fetchers are just async functions, so they run fine under prerender. Mark the route prerenderable and SvelteKit will call the load at build time:

src/routes/blog/+page.server.ts
import { fetchArticles } from "@ghostwritr/svelte";
import { env } from "$env/dynamic/private";
export const prerender = true;
export async function load() {
return { articles: await fetchArticles({ siteId: env.GHOSTWRITR_SITE_ID }) };
}

For dynamic [slug] routes, give the prerenderer the list of slugs from an entries export so it knows which pages to build:

src/routes/blog/[slug]/+page.server.ts
import { error } from "@sveltejs/kit";
import { fetchArticles, fetchArticle } from "@ghostwritr/svelte";
import { env } from "$env/dynamic/private";
export const prerender = true;
export async function entries() {
const articles = await fetchArticles({ siteId: env.GHOSTWRITR_SITE_ID });
return articles.map((a) => ({ slug: a.slug }));
}
export async function load({ params }) {
const article = await fetchArticle({
siteId: env.GHOSTWRITR_SITE_ID,
slug: params.slug,
});
if (!article) throw error(404, "Not found");
return { article };
}

Outside SvelteKit there’s no loadawait the fetcher in your server entry or data loader and pass siteId from import.meta.env:

import { fetchArticle } from "@ghostwritr/svelte";
const article = await fetchArticle({
siteId: import.meta.env.VITE_GHOSTWRITR_SITE_ID,
slug,
});
if (!article) {
// render your 404
}

fetchArticle returns null for an unknown slug; that’s a routing miss, so convert it to a 404 with error(404). Everything else — a missing feed (unseeded siteId), a network failure, a bad response — throws a typed GhostwritrError with a stable code (NOT_FOUND fails closed rather than returning an empty list). See Error handling and Error handling concepts.