TypeScript client
One dependency-free file: list, paginate, read by slug, typed errors. Runs anywhere fetch does.
lib/seoforsaas/client.ts wraps the read endpoints. It needs only fetch (Node 18+, Bun, Deno, Cloudflare Workers) and ships with types.ts, the whole API as TypeScript types. It's a source file in your repository, not a package: read it, and extend it through its options rather than edits, so reinstalling a newer version stays a clean overwrite.
npx shadcn@latest add https://seoforsaas.dev/r/client.jsoncreateClient(options)
import { createClient } from "./lib/seoforsaas/client";const seoforsaas = createClient({ projectId: process.env.SEOFORSAAS_PROJECT_ID, contentKey: process.env.SEOFORSAAS_CONTENT_KEY,});| Option | Type | |
|---|---|---|
projectId | string | undefined | Delivery → Connection. Required. |
contentKey | string | undefined | Required. |
apiUrl | string | Defaults to https://api.seoforsaas.dev. |
requestInit | RequestInit without headers, method, body and signal | Merged into every request: framework cache options go here, for example { cache: "force-cache", next: { tags: ["seoforsaas"] } } in Next.js. |
timeoutMs | number | Per request. Defaults to 10000. |
projectId and contentKey accept undefined so you can pass environment variables as they are: a missing one throws at once, naming the variable.
Methods
| Method | Returns | |
|---|---|---|
listArticles({ page?, limit? }) | { articles: Article[], pagination } | One page, newest first. limit defaults to 100 here (the API's own default is 20) and is capped at 100. |
listAllArticles() | Article[] | Every page. For indexes and static params. |
getArticle(slug) | ArticleDetail | null | With related (always an array); null when there's no published article at that slug. |
getSitemap() | SitemapEntry[] | { url, lastModified } per published article, newest change first. See Sitemap. |
The methods are plain functions: destructuring them (const { getArticle } = seoforsaas) works.
Errors
A non-2xx response other than a missing article throws SeoForSaasError with the HTTP status and the API's error code. A timeout throws the platform's TimeoutError, and a network failure its TypeError, unchanged:
import { createClient, SeoForSaasError } from "./lib/seoforsaas/client";const seoforsaas = createClient({ projectId: process.env.SEOFORSAAS_PROJECT_ID, contentKey: process.env.SEOFORSAAS_CONTENT_KEY,});export async function loadArticles() { try { return await seoforsaas.listAllArticles(); } catch (error) { if (error instanceof SeoForSaasError && error.status === 401) { throw new Error("SEOFORSAAS_CONTENT_KEY is wrong or was revoked", { cause: error }); } throw error; }}Codes are listed under Errors and limits.