Getting Started
Installation
pnpm add laikacmsBasic Example
import { buildJsonApi } from 'laikacms/storage-api';
import { FileSystemStorageRepository } from 'laikacms/storage-fs';
import { rawSerializer } from 'laikacms/storage-serializers-raw';
const repo = new FileSystemStorageRepository('./content', { md: rawSerializer }, 'md');
const api = buildJsonApi({ repo });
export default { fetch: api.fetch };⚠️ No authentication by default:
buildJsonApiperforms no authentication unless you give it one — any client can create, read, update, and delete content without a token. Do not expose it directly to an untrusted network. You have two options:
- For a production-ready API with built-in auth, use
decapApifrom@laikacms/decapinstead.- For custom authorization, pass an
authorizecallback (see below). It runs once per action — receiving the action name, its direct arguments, and the wholeRequest— and returnstrueto allow,falseto deny with a 403, or aLaikaErrorto deny with a custom status.typescriptimport { AuthenticationError, ForbiddenError } from 'laikacms/core'; const api = buildJsonApi({ repo, authorize: async ({ action, request }) => { const token = request.headers.get('Authorization')?.replace('Bearer ', ''); const user = token ? await lookupUser(token) : undefined; if (!user) return new AuthenticationError('Missing or invalid token'); // → 401 // Reads for everyone, writes for editors only. const isWrite = action !== 'getObject' && action !== 'getFolder' && !action.startsWith('list'); if (isWrite && !user.canEdit) return new ForbiddenError('Editors only'); // → 403 return true; }, });The same
authorizeoption is available on the documents API (laikacms/documents/api), the contentbase settings API (laikacms/contentbase-api), and the assets API (laikacms/assets-api). Atomic-operation requests authorize each sub-action up front, so a single denial rejects the whole batch before any write runs.
Note:
rawSerializerstores only thebodyfield of each content object as plain text. Passing any other fields (e.g.title,tags) will throw an error at write time to prevent silent data loss. If you need to persist multi-field content, usejsonSerializerinstead.
Content in laikacms is always an object, not a raw string — the convention is to wrap raw text as
{ "body": "..." }. See Content Model → thebodyconvention.
Cloudflare Workers
import { buildJsonApi } from 'laikacms/storage-api';
import { R2StorageRepository } from 'laikacms/storage-r2';
import { rawSerializer } from 'laikacms/storage-serializers-raw';
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const repo = new R2StorageRepository(env.CONTENT_BUCKET, { md: rawSerializer }, 'md');
return buildJsonApi({ repo }).fetch(request);
},
};With Decap CMS
See Decap Integration.
Next Steps
- Architecture - Design patterns
- JSON:API Reference - Endpoints
- Packages - All packages
- Deployment - Production setup