@craftware/crafty-sdk
v0.1.0
Published
Official JavaScript SDK for Crafty CMS public API
Readme
@craftware/crafty-sdk
Official JavaScript SDK for the Crafty public API (current local implementation).
Requirements
- Node
>=20(package engine) - Or any runtime with
fetchavailable (browser, Bun, Deno, etc.)
Create a client
import { createCraftyClient } from "@craftware/crafty-sdk";
const crafty = createCraftyClient({
baseUrl: "https://tenant.crafty.test",
apiKey: "SITE_API_TOKEN",
timeoutMs: 10_000
});Client config
baseUrl(required): tenant base URL. The SDK appends/api/v1automatically if missing.apiKey(optional): site API token sent asX-API-Key.- When provided, the API resolves the active site from this token.
pages.*requests use this site context automatically (you do not passsiteSlug).
fetch(optional): custom fetch implementation.headers(optional): default headers applied to all requests.timeoutMs(optional): default timeout for all requests.
Available resources
collections
crafty.collections.list(options?)crafty.collections.get(slug, options?)
entries
crafty.entries.list(collectionSlug, query?, options?)crafty.entries.get(collectionSlug, id, options?)
Supported common query params:
sort_by(any sortable field, including collection fields liketitle)sort_order(asc|desc)per_pagepage- Additional filters as key/value pairs (passed through to the API)
pages
crafty.pages.list(query?, options?)crafty.pages.get(pageSlug, options?)
pages.* endpoints are scoped by the apiKey configured on the client.
pages.get() returns a page response enriched with block helpers:
page.blocks(array alias ofpage.data.blocks ?? [])page.block(type)=> first block of that type ornullpage.blocksOf(type)=> all blocks of that typepage.hasBlock(type)=> booleanpage.requireBlock(type)=> first block or throwspage.byType.<type>=> array of blocks for that typepage.firstByType.<type>=> first block for that type ornull
mediaCollections
crafty.mediaCollections.list(query?, options?)
Supported query.include values:
"stats""directories"- Array form is supported and serialized as comma-separated values
mediaFiles
crafty.mediaFiles.list(query?, options?)
Supported common query params:
collectiondirectory_idttl_minutesper_pagepage
Request options (per call)
Every method accepts options? as the last argument:
signal:AbortSignalheaders: request-specific headers (merged with client headers)timeoutMs: overrides client timeout for that call
const posts = await crafty.entries.list(
"posts",
{ per_page: 15, page: 1, sort_by: "updated_at", sort_order: "desc" },
{ timeoutMs: 5_000 }
);Responses (current shapes)
collections.list()=>{ data: CraftyCollection[] }collections.get()=>CraftyCollectionentries.list()=> paginated{ data, links, meta }entries.get()=>{ data: CraftyEntry }pages.list()=> paginated{ data, links, meta }pages.get()=>{ data: CraftyPage }mediaCollections.list()=>{ data, meta: { includes } }mediaFiles.list()=>{ data, links, meta }(metaincludesttl_minutes)
Errors
The SDK throws CraftyError for HTTP, timeout, abort, and network failures.
import { CraftyError } from "@craftware/crafty-sdk";
try {
await crafty.pages.get("home");
} catch (error) {
if (error instanceof CraftyError) {
console.error(error.code, error.status, error.message);
console.error(error.requestId); // if API sent x-request-id
}
}Current error codes:
NETWORK_ERRORTIMEOUTBAD_REQUESTUNAUTHORIZEDFORBIDDENNOT_FOUNDRATE_LIMITEDSERVER_ERRORUNKNOWN_ERROR
End-to-end example
import { createCraftyClient } from "@craftware/crafty-sdk";
const crafty = createCraftyClient({
baseUrl: "https://twocare.crafty.test",
apiKey: process.env.CRAFTY_API_KEY,
timeoutMs: 10_000
});
const collections = await crafty.collections.list();
const posts = await crafty.entries.list("posts", { per_page: 15 });
const mediaCollections = await crafty.mediaCollections.list({ include: ["stats", "directories"] });
const mediaFiles = await crafty.mediaFiles.list({ collection: "library", per_page: 24 });
const homePage = await crafty.pages.get("home");Pages scoping (site via API key)
pages requests no longer require a siteSlug parameter. The site is inferred server-side from the
X-API-Key header (set from apiKey in createCraftyClient()).
const crafty = createCraftyClient({
baseUrl: "https://tenant.crafty.test",
apiKey: "SITE_API_TOKEN",
});
await crafty.pages.list({ per_page: 12 });
const landing = await crafty.pages.get("landing");
const processBlock = landing.block("process");
const allProcessBlocks = landing.blocksOf("process");
const processFirst = landing.firstByType.process;
const processList = landing.byType.process ?? [];Notes
- The SDK normalizes
baseUrland avoids duplicating/api/v1. Accept: application/jsonis set automatically unless overridden.src/types/generated.tsis still a placeholder until OpenAPI codegen is wired.- Public
blockTypesendpoints are not exposed yet; block-related types are currently hand-modeled.
