Skip to content

storyblok-js-client

storyblok-js-client is a universal JavaScript client for both of Storyblok’s APIs. A single StoryblokClient instance targets the Content Delivery API when initialized with an access token, or the Management API when initialized with a personal access token. The client offers in-memory caching, automatic rate limit, and retry handling.

  • Node.js 18 or higher
  • The latest versions of Chrome, Edge, Firefox, and Safari

The client calls the native fetch API. In environments that lack it, import isomorphic-fetch at the entry point of the application to provide it:

import "isomorphic-fetch";

Internet Explorer support is available in version 4 of the package.

Terminal window
npm install storyblok-js-client

To read content, initialize the client with a preview or public access token from Settings → Access Tokens:

import StoryblokClient from "storyblok-js-client";
const Storyblok = new StoryblokClient({
accessToken: "YOUR_SPACE_ACCESS_TOKEN",
});
const { data } = await Storyblok.get("cdn/stories/home", {
version: "draft",
});

To manage content, initialize the client with a personal access token instead:

import StoryblokClient from "storyblok-js-client";
const Storyblok = new StoryblokClient({
oauthToken: "YOUR_PERSONAL_ACCESS_TOKEN",
});
await Storyblok.post(`spaces/${spaceId}/stories`, {
story: { name: "About", slug: "about" },
});

The following sections document the StoryblokClient constructor and each method it exposes.

Creates a client instance.

const Storyblok = new StoryblokClient(CONFIG, ENDPOINT);

CONFIG accepts the following options:

Option Description
accessToken A preview or public access token. Required for Content Delivery API requests.
oauthToken A personal access token. Required for Management API requests.
region The space’s region: eu (default), us, ap, ca, or cn. Required for spaces outside the EU.
cache Cache configuration object. Refer to Caching.
rateLimit A fixed rate limit in requests per second that overrides the automatic limits. Refer to Rate limit.
maxRetries The number of retries after a 429 response. Defaults to 10.
retriesDelay The delay in milliseconds between retries. Defaults to 300.
timeout Request timeout in milliseconds.
responseInterceptor A function that receives and returns the response. Refer to Response interceptors.
resolveNestedRelations Whether to resolve nested relations. Defaults to true.
https Whether to call the API over HTTPS.

ENDPOINT optionally overrides the API base URL.

Fetches a single entry or one page of entries. Listings return 25 entries per page by default. The maximum is 100 entries per page for stories, and 1000 for the other endpoints.

Storyblok.get(SLUG, PARAMS, FETCH_OPTIONS);
Parameter Description
SLUG Required. The path to request, such as cdn/stories, cdn/links, cdn/tags, or cdn/datasources.
PARAMS Query parameters. Refer to the Content Delivery API.
FETCH_OPTIONS Fetch options. Refer to Custom fetch options.
const { data } = await Storyblok.get("cdn/stories/home", {
version: "draft",
});

Fetches every entry across all pages and returns them as a single array. The client pages through the endpoint internally, so a listing larger than the page maximum needs no pagination code.

Storyblok.getAll(SLUG, PARAMS, ENTITY, FETCH_OPTIONS);
Parameter Description
SLUG Required. The path to request, such as cdn/stories, cdn/links, cdn/tags, or cdn/datasources.
PARAMS Required. Query parameters. Refer to the Content Delivery API.
ENTITY The entity to read from the response, such as stories, links, or datasource_entries.
FETCH_OPTIONS Fetch options. Refer to Custom fetch options.

Unlike get(), this method resolves to the array of entries rather than to a response object:

const stories = await Storyblok.getAll("cdn/stories", {
version: "draft",
starts_with: "articles/",
});

Write to the Management API. Each method requires a client initialized with an oauthToken.

Storyblok.post(SLUG, PARAMS, FETCH_OPTIONS);
Storyblok.put(SLUG, PARAMS, FETCH_OPTIONS);
Storyblok.patch(SLUG, PARAMS, FETCH_OPTIONS);
Storyblok.delete(SLUG, PARAMS, FETCH_OPTIONS);

SLUG is a Management API path and PARAMS is the request body.

await Storyblok.post(`spaces/${spaceId}/stories`, {
story: { name: "About", slug: "about" },
});
await Storyblok.put(`spaces/${spaceId}/stories/${storyId}`, {
story: { name: "About us", slug: "about-us" },
});
await Storyblok.delete(`spaces/${spaceId}/stories/${storyId}`);

Empties the client’s cache and returns the client.

Storyblok.flushCache();

Removes a previously configured response interceptor.

Storyblok.ejectInterceptor();

The client can cache responses in memory. Without a clear strategy, the cache persists until flushCache() runs.

const Storyblok = new StoryblokClient({
accessToken: "YOUR_SPACE_ACCESS_TOKEN",
cache: {
clear: "auto",
type: "memory",
},
});
Option Values Description
type none, memory, custom Where to cache responses.
clear manual, auto, onpreview When the cache is empty. Defaults to manual. onpreview clears only on draft requests.
cv auto, manual Whether the client tracks the content version. Defaults to auto.

With cv: 'auto', the client stores the cv value the Content Delivery API returns and appends it to subsequent published requests, which keeps responses served from the CDN cache. Set cv: 'manual' where an external system handles invalidation, or in server-side and edge caching setups where a tracked cv would prevent cache reuse. Learn more in the caching concept.

To store the cache elsewhere (such as Redis), set type: 'custom' with four methods:

new StoryblokClient({
accessToken: "YOUR_SPACE_ACCESS_TOKEN",
cache: {
clear: "manual",
type: "custom",
custom: {
get() {
return Promise.resolve(0);
},
getAll() {
return Promise.resolve(0);
},
set() {
return Promise.resolve(0);
},
flush() {
return Promise.resolve(0);
},
},
},
});

The client queues requests to stay within the rate limits of the Content Delivery API and the Management API, with no configuration required. It derives the rate from per_page and adjusts it to the rate limit headers the API returns.

Set rateLimit to apply one fixed rate to every request instead, up to a maximum of 1000 per second:

const Storyblok = new StoryblokClient({
accessToken: "YOUR_SPACE_ACCESS_TOKEN",
rateLimit: 25,
});

A request answered from the in-memory cache skips the queue, since it never reaches the API.

A 429 response triggers an automatic retry:

const Storyblok = new StoryblokClient({
accessToken: "YOUR_SPACE_ACCESS_TOKEN",
maxRetries: 10,
retriesDelay: 300,
});

get(), getAll(), post(), put(), patch(), delete(), getStory(), and getStories() accept a final argument matching the Fetch API’s RequestInit object, minus method, which the client sets itself.

const { data } = await Storyblok.get("cdn/stories/home", { version: "draft" }, { mode: "cors", cache: "no-cache" });

A response interceptor receives every response before the caller does. It must return the response.

const Storyblok = new StoryblokClient({
accessToken: "YOUR_SPACE_ACCESS_TOKEN",
responseInterceptor: (response) => {
if (response.status === 200) {
// Handle the response
}
return response;
},
});

Call ejectInterceptor() to remove it.

A failed request throws an object rather than a plain Error:

interface ISbError {
message: Error;
status: number;
response: ISbResponse;
}
interface ISbResponse {
data: any;
status: number;
statusText: string;
headers: any;
config: any;
request: any;
}
try {
const { data } = await Storyblok.get("cdn/stories/home", { version: "draft" });
} catch (error) {
console.log(error.status, error.message);
}

resolveRelations() resolves referenced stories in the Storyblok Bridge’s input event, so live editing reflects referenced content. It resolves entries up to two levels deep.

window.storyblok.on("input", (event) => {
window.storyblok.addComments(event.story.content, event.story.id);
window.storyblok.resolveRelations(event.story, ["post.author", "post.categories"], () => {});
});

Was this page helpful?

What went wrong?

This site uses reCAPTCHA and Google's Privacy Policy (opens in a new window).Terms of Service (opens in a new window) apply.