Below is a starting point for a Node.js script that checks for out-of-schema fields in Storyblok content. It loads all stories from the Content Delivery API, fetches all component definitions from the Management API, and compares the fields in each story’s content against its component schema. Any fields present in the content but missing from the schema are reported as out-of-schema.
Important: This is meant to customize to fit your own topic but should give you an outline of what is needed to check.
- Node.js 18+ (for native fetch support)
storyblok-js-client
package- Your Storyblok space access token (for Content Delivery API)
- Your Storyblok OAuth token (for Management API)
- Your space ID
Install the package:
npm install storyblok-js-client
// Required dependencies
import StoryblokClient from 'storyblok-js-client';
const SPACE_ID = '<YOUR_SPACE_ID>';
const CDN_TOKEN = '<YOUR_CDN_TOKEN>'; // Content Delivery API token
const OAUTH_TOKEN = '<YOUR_OAUTH_TOKEN>'; // Management API OAuth token
// Initialize Storyblok clients
const cdnClient = new StoryblokClient({ accessToken: CDN_TOKEN });
const mgmtClient = new StoryblokClient({ oauthToken: OAUTH_TOKEN });
// Helper: fetch all stories (paginated)
// IMPORTANT: FILTER ACCORDINGLY TO NOT LOAD ALL STORIES IN MEMORY - ONLY LOAD THE STORIES YOU ACTUALLY NEED
async function fetchAllStories() {
let stories = [];
let page = 1;
let total = 1;
while (stories.length < total) {
// Adapt this request to the stories you actually want to check.
const res = await cdnClient.get('cdn/stories', { page, per_page: 25, starts_with: '/folder_slug_goes_here', version: 'draft' });
stories = stories.concat(res.data.stories);
total = res.total;
page++;
}
return stories;
}
// Helper: fetch all components (not paginated)
async function fetchAllComponents() {
const res = await mgmtClient.get(`spaces/${SPACE_ID}/components`);
// Map by component name for easy lookup
const componentsByName = {};
res.data.components.forEach(component => {
componentsByName[component.name] = component;
});
return componentsByName;
}
// Helper: get schema field names from component definition
function getSchemaFieldNames(component) {
if (!component.schema) return [];
return Object.keys(component.schema);
}
// Main logic
(async () => {
const stories = await fetchAllStories();
const componentsByName = await fetchAllComponents();
for (const story of stories) {
const { content } = story;
if (!content || !content.component) continue;
const componentName = content.component;
const componentDef = componentsByName[componentName];
if (!componentDef) {
console.warn(`Component definition not found for: ${componentName}`);
continue;
}
const schemaFields = getSchemaFieldNames(componentDef);
const contentFields = Object.keys(content).filter(
key => key !== 'component' // Exclude the component field itself
);
// Find fields in content but not in schema
const outOfSchemaFields = contentFields.filter(
field => !schemaFields.includes(field)
);
if (outOfSchemaFields.length > 0) {
console.log(
`Story "${story.name}" (slug: ${story.slug}, component: ${componentName}) has out-of-schema fields:`,
outOfSchemaFields
);
}
}
})();
- Loads all stories (please set filters accordingly) using the Content Delivery API (
cdn/stories
) and paginates if necessary. - Loads all component definitions using the Management API (
spaces/{space_id}/components
). - For each story, gets the list of fields in the
content
property and compares them to the schema fields defined in the component. - Reports any fields in the story content that are not present in the component schema.
- Replace
YOUR_SPACE_ID
,YOUR_CDN_TOKEN
, andYOUR_OAUTH_TOKEN
with your actual credentials. IMPORTANT: DO NOT COMMIT OR PUBLISH YOUR OAUTH TOKENS AS IT GRANTS ACCESS TO YOUR SPACES - Replace
folder_slug_goes_here
with either your own folder or adapt the filter to only load stories you want to check. - The script assumes that top-level fields in the
content
object are schema fields. For nested components (e.g., in bloks fields), you may need to extend the script to recurse into those arrays for a deeper audit. - The Management API requires an OAuth token, not a CDN token.
This script provides a foundation for detecting out-of-schema fields in your Storyblok content, please adapt to your own needs and utilize our Management API Documentation and Content Delivery API Documentation to adapt.