Reference
Assets, error handling, caching, and the full type and utility-function reference for @localess/client.
Assets
assetLink(asset, params?)
Generate a fully qualified URL for a content asset. Pass params to resize or convert the image — see Image Transforms.
import { localessClient } from "@localess/client";
const client = localessClient({ origin, spaceId, token });
// From a ContentAsset object
const url = client.assetLink(content.data.image);
// From the asset's uri (its ID, not a path)
const url = client.assetLink(content.data.image.uri);
// Resized and converted
const thumb = client.assetLink(content.data.image, { w: 400, f: 'avif' });assetOriginalLink(asset) and assetDownloadLink(asset)
A bare assetLink(asset) URL is a rendition, not the uploaded file: still images are re-encoded at their format's default quality even with no parameters. When you need the exact bytes that were uploaded, or want the browser to save the file instead of displaying it, use these two methods:
const originalUrl = client.assetOriginalLink(content.data.image); // the file exactly as uploaded
const downloadUrl = client.assetDownloadLink(content.data.image); // served as an attachmentNeither accepts transform parameters. A request that adds any is rejected with 400. Downloads keep non-ASCII file names intact.
Error Handling
getLinks, getContentBySlug, getContentById, and getTranslations throw instead of returning empty data on failure. Two error classes cover the two ways a request fails — both carry attempts, because failures are retried first:
LocalessApiError— the API answered with a non-2xx status. Carriesstatus,statusText,url, the parsed errorbody, and ahint.LocalessNetworkError— no response arrived (DNS failure, refused connection, TLS error). Carriesorigin,url,hint, and the underlying error ascause.
Always wrap calls in try/catch:
import { LocalessApiError, LocalessNetworkError } from "@localess/client";
try {
const content = await client.getContentBySlug('home');
} catch (error) {
if (error instanceof LocalessApiError) {
console.error(error.status, error.statusText, error.hint);
} else if (error instanceof LocalessNetworkError) {
console.error(`Could not reach ${error.origin}`, error.cause);
}
}Timeouts and Retries
Every request times out after timeoutMs — 15 seconds by default. Node's fetch has no timeout of its own, so without one a hung connection can stall a static build indefinitely.
Failed requests are retried with exponential backoff and full jitter, honouring a server's Retry-After up to maxDelayMs:
- Retried — network failures, and the statuses in
retryStatuses(default408,429,500,502,503,504). - Thrown immediately — every other status, including
401,403and404.
Each attempt gets its own timeout, so the worst case is roughly attempts × timeoutMs plus backoff. Every fetching method also accepts a signal to cancel the call; an abort through it is not retried.
const client = localessClient({ origin, spaceId, token, timeoutMs: 5000, retry: { attempts: 5 } });
const content = await client.getContentBySlug('home', { signal: AbortSignal.timeout(10_000) });retry option | Default | Description |
|---|---|---|
attempts | 3 | Maximum requests per call, including the first. 1 disables retrying |
baseDelayMs | 300 | Base delay for exponential backoff |
maxDelayMs | 5000 | Upper bound on any single delay, including a Retry-After |
retryStatuses | [408, 429, 500, 502, 503, 504] | Response statuses worth retrying |
Caching
All API responses are cached by default using a TTL (time-to-live) cache. The cache key is the request URL with the token removed and its parameters sorted. You can configure caching when initializing the client.
// Default: 5-minute TTL cache
const client = localessClient({ origin, spaceId, token });
// Custom TTL in seconds (e.g., 10 minutes)
const client = localessClient({ origin, spaceId, token, cacheTTL: 600 });
// Disable caching entirely
const client = localessClient({ origin, spaceId, token, cacheTTL: false });Custom cache
Pass any object with get, set and has as cache. The methods may return promises, so a Redis- or KV-backed cache works as is:
import { localessClient, type ICache } from "@localess/client";
const cache: ICache<unknown> = {
get: (key) => redis.get(key).then((v) => (v ? JSON.parse(v) : undefined)),
set: (key, value) => redis.set(key, JSON.stringify(value)).then(() => undefined),
has: (key) => redis.exists(key).then((n) => n > 0),
};
const client = localessClient({ origin, spaceId, token, cache });Cache keys don't include the token. A cache instance shared by two clients is shared across their tokens. That is fine when both tokens have the same permissions — but don't share one between a draft-capable token and a published-only one, or the published-only client is served cached drafts instead of getting a
403.
Framework fetch options
fetchInit passes framework options — such as Next.js's next: { revalidate } or a standard cache mode — into every fetch, and each fetching method accepts the same option per call. A request that carries fetchInit bypasses the client's own cache entirely, so the framework owns caching for it and a framework-level revalidation isn't masked by a second cache layer.
Type Reference
Content<T>
interface Content<T extends ContentData> extends ContentMetadata {
locale: string; // The locale actually served — may be the space's fallback
data?: T;
links?: Links; // Populated when resolveLink: true
references?: References; // Populated when resolveReference: true
assets?: Assets; // Populated when resolveAsset: true
}ContentMetadata
interface ContentMetadata {
id: string;
name: string;
kind: 'FOLDER' | 'DOCUMENT';
slug: string;
fullSlug: string;
parentSlug: string;
publishedAt?: string;
createdAt: string;
updatedAt: string;
}ContentData
Base type for all content schema data objects.
interface ContentDataSchema {
_id: string;
_schema: string;
}
interface ContentData extends ContentDataSchema {
[field: string]: ContentDataField | undefined;
}ContentAsset
interface ContentAsset {
kind: 'ASSET';
uri: string;
}ContentLink
interface ContentLink {
kind: 'LINK';
type: 'url' | 'content';
target: '_blank' | '_self';
uri: string;
}ContentReference
interface ContentReference {
kind: 'REFERENCE';
uri: string;
}ContentRichText
interface ContentRichText {
type?: string;
content?: ContentRichText[];
}Other Types
Links— A key-value map of content IDs toContentMetadataobjects.References— A key-value map of reference IDs toContentobjects.Translations— A key-value map of translation keys to translated string values.Assets— A key-value map of asset IDs to asset metadata (name,type,alt,width,height,size,duration).AssetTransformParams— Theparamsaccepted byassetLink— see Image Transforms.
Utility Functions
| Function | Returns | Description |
|---|---|---|
isBrowser() | boolean | Returns true if code is running in a browser environment |
isServer() | boolean | Returns true if code is running in a server/Node.js environment |
isIframe() | boolean | Returns true if the page is rendered inside an iframe |
isBrowser,isServer,isIframe,loadLocalessSync,localessEditableandlocalessEditableFieldstill work from@localess/client, but they are deprecated re-exports and will be removed in a future major. Import them from@localess/live-previewinstead.
findLink(links, link)
Resolves a ContentLink field to a path or URL. Pass the links map from a response fetched with resolveLink: true:
import { findLink } from "@localess/client";
const href = findLink(content.links, content.data.cta.link);
// content link → '/' + fullSlug (or '/not-found' if missing from the map); url link → the URL as isVisual Editor
Enable and subscribe to Localess Visual Editor live-editing events from a plain TypeScript app with @localess/live-preview.
Getting Started
Nuxt module for Localess — one config block for content delivery, component auto-registration, a server-only client for draft content, and Visual Editor integration.