The official client for reading your content from the Iris platform. Iris is where your site's content is managed and edited; this package lets your site fetch that content and render it however you like, with full TypeScript types.
npm install @ravisiontech/iris-client
# or
pnpm add @ravisiontech/iris-client
# or
yarn add @ravisiontech/iris-clientimport { createIris } from "@ravisiontech/iris-client";
const iris = createIris({ apiKey: process.env.IRIS_KEY! });
const home = await iris.pages.bySlug("home");
const all = await iris.pages.list();Pass baseUrl to target a local Iris during development:
const iris = createIris({
apiKey: process.env.IRIS_KEY!,
baseUrl: "http://localhost:3000",
});Requests are scoped to the property the API key belongs to, so pages.list() only ever
returns that property's pages.
A page's content is an array of blocks, each discriminated by blockType. Use
getBlocks to iterate and narrow each block to its concrete shape:
import { getBlocks, type Page } from "@ravisiontech/iris-client";
function render(page: Page) {
return getBlocks(page).map((block) => {
switch (block.blockType) {
case "hero":
return block.heading; // typed as the hero block
case "faq":
return block.items;
default:
return null;
}
});
}BlockOfType<"hero"> narrows to a single block type when you want per-block components.
| Method | Returns |
|---|---|
iris.pages.list(opts?, init?) |
Paginated<Page> |
iris.pages.bySlug(slug, opts?, init?) |
Page | null |
iris.pages.byId(id, opts?, init?) |
Page |
iris.media.byId(id, opts?, init?) |
Media |
iris.raw.get(path, params?, init?) |
raw JSON (escape hatch) |
opts accepts depth, limit, page, sort, where, and draft. init is an
optional RequestInit
spread onto the underlying fetch call — see Caching & fetch options.
Every read method takes an optional final init argument (typed as RequestOptions, an
alias for the native RequestInit). It is spread onto the underlying fetch, so you can
control caching, cancellation, and headers per request:
// Cancel with an AbortController
const controller = new AbortController();
const page = await iris.pages.bySlug("home", {}, { signal: controller.signal });
// Opt out of caching
const fresh = await iris.pages.list({}, { cache: "no-store" });Framework-specific fields pass through too, such as Next.js ISR revalidation:
// Next.js App Router — revalidate this fetch every 60s
const home = await iris.pages.bySlug("home", {}, { next: { revalidate: 60 } });Any headers you pass in init are merged over the defaults, so you can add your own
without overriding Authorization.
Failed requests throw a typed error carrying the response status and body. There are
two concrete types, both extending the IrisError base, so you can handle a billing
suspension differently from a transient server fault:
IrisBillingError— the API responded402or403(subscription/billing or authentication restriction). Treat this as a deliberate suspension.IrisServerError— any other non-2xx response (network fault,5xx, etc.). Treat this as transient.
import {
IrisBillingError,
IrisServerError,
type Page,
} from "@ravisiontech/iris-client";
async function getHome(): Promise<Page | null> {
try {
return await iris.pages.bySlug("home");
} catch (err) {
if (err instanceof IrisBillingError) {
// Account suspended — drop cached content for this property.
throw err;
}
if (err instanceof IrisServerError) {
// Transient — fall back to the last good cache.
return null;
}
throw err;
}
}Catching the IrisError base still matches both, if you don't need to distinguish them.
Every type you need ships with the package. Import Page, Media, and the block unions
(PageBlock, BlockOfType) directly:
import type { Media, Page, PageBlock } from "@ravisiontech/iris-client";They always reflect the content types set up in your Iris platform, so your editor autocompletes real fields and flags typos before you ship.