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 firstconst articles = await fetchArticles({ siteId });
// one by slug — or null when it isn't in the feedconst article = await fetchArticle({ siteId, slug });In a SvelteKit server load
Section titled “In a SvelteKit server load”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:
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:
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 };}Reading siteId from the environment
Section titled “Reading siteId from the environment”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:
| Context | Source |
|---|---|
Server load (+page.server.ts) | $env/dynamic/private → env.GHOSTWRITR_SITE_ID |
| Static (known at build) | $env/static/private → GHOSTWRITR_SITE_ID |
| Build / prerender script | process.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:
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 }) };}Prerendering (SSG)
Section titled “Prerendering (SSG)”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:
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:
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 };}Plain Svelte (Vite)
Section titled “Plain Svelte (Vite)”Outside SvelteKit there’s no load — await 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}null vs. throw
Section titled “null vs. throw”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.
Related
Section titled “Related”- Keyless reads — why your
siteIdis the only key. - The content feed — what’s behind
feeds.ghostwritr.io/{siteId}/.