prelo
Docs

Configuration & schema

One small file defines the whole CMS: cms.config.js.

cms.config.js

module.exports = {
siteName: "Noodleverse",
port: 3300,
site: "./web/site.js", // the public site, served by this process — delete to go headless
types: { … }, // content model — see below
globals: { … }, // nav, footer — singletons
webhooks: { onPublish: "https://…/api/revalidate" },
};

One file, checked into the repo. Everything is optional; the defaults run a client site out of the box. Change it and save — under prelo dev the server restarts itself and the API and studio update (production prelo start needs a manual restart) — and prelo check validates it and fails loudly with the exact fix.

The site module

site: points at the module that serves / — same process and port as /api and /studio. Two shapes, detected automatically: export a function (req, res) and the CMS mounts it as a request handler (Next, Astro, anything Express-compatible), or export { handle(url, ctx), css } for the starter's GET-only render contract. Omit site: to go fully headless — then siteUrl: tells /api/branding where the live site is. prelo dev watches the config and the site module and restarts on change. Full contracts and examples: Embed & one process.

Content types

types: {
// post + page are built in
menuItem: {
label: "Menu items",
fields: {
price: { type: "number" },
photo: { type: "image" },
heat: { type: "select", options: ["mild", "wild"] },
description: { type: "richtext" },
},
},
},

Each type gets a studio panel and API endpoints automatically — adding one is a config edit, never a code change. Field kinds: text, richtext, number, image, slug, select, list.

Globals and roles

Globals (nav, footer, contact details) are singleton types declared the same way. Roles are fixed and simple on purpose: admin manages users and settings, editor publishes, author drafts, viewer reads private types. API keys carry a role like any user — see Content API.