Quick Start
Get a timbl CMS running and queryable from zero. You need Bun installed.
1. Install and run
bun installbun run devThe server starts on http://localhost:3000. Open http://localhost:3000/docs for the auto-generated API docs (Scalar UI), or http://localhost:3000/openapi.json for the raw OpenAPI document.
2. Define a schema
Create content.config.ts in your project root. This is where you define your content model.
import { defineCMS, defineCollection, defineGlobal } from "timbl";
export default defineCMS({ collections: [ defineCollection({ key: "posts", labels: { singular: "Post", plural: "Posts" }, fields: [ { key: "title", type: "text", required: true }, { key: "slug", type: "slug", required: true }, { key: "body", type: "markdown" }, { key: "status", type: "select", options: ["draft", "published"] }, ], }), ], globals: [ defineGlobal({ key: "siteSettings", label: "Site Settings", fields: [{ key: "siteName", type: "text" }], }), ],});The dev server picks up config changes on restart. See Configuration for the full schema builder API, and Field Types for every field option.
3. Create a user
Writes need a session. Register a user and save the cookie for the next step:
curl -c cookies.txt -X POST http://localhost:3000/api/cms/auth/actions/register \ -H "Content-Type: application/json" \ -d '{"name":"Editor","email":"editor@example.com","password":"correct horse battery staple"}'4. Create content
Use the saved cookie to authenticate the write:
curl -b cookies.txt -X POST http://localhost:3000/api/posts \ -H "Content-Type: application/json" \ -d '{"title":"Hello","slug":"hello","status":"published","body":"# Hello"}'5. Query content
Reads are public by default, no cookie needed:
curl http://localhost:3000/api/posts?status=publishedYou get JSON with collection, total, limit, offset, and items. See the HTTP API Reference for query parameters, filters, and relation expansion.
6. Use the client SDK
For a typed frontend, install @timbl/client and infer types from your config:
import { createTimblClient, type InferCMS } from "@timbl/client";import type cms from "./content.config";
const client = createTimblClient<InferCMS<typeof cms>>({ baseUrl: "http://localhost:3000",});
const posts = await client.collection("posts").findMany({ status: "published" });const post = await client.collection("posts").findBySlug("hello");const settings = await client.global("siteSettings").get();The client ships zero runtime dependencies. It consumes your config as a type-only import, so @timbl/core never reaches your frontend bundle. See the Client SDK Reference for the full method list.
Next
- Configuration: collections, globals, hooks, runtime config
- Field Types: every built-in field type and its options
- HTTP API Reference: endpoints, query params, error shapes
- Astro Kitchen Sink: a full Astro app exercising the client