Overview
@floggy/cms is the single dependency you install to build a blog on Floggy. It reads your content fully typed and ready to render - html, markdown, SEO metadata, JSON-LD, and feeds - and, with an flg_ key, it writes: create a draft from Markdown, preview it, publish it. Zero runtime dependencies.
Install
Quickstart
A working blog index plus a post page, with SEO, in about 15 lines.
import { createClient } from "@floggy/cms";
const floggy = createClient({ project: "projecta" });
// Blog index
const { posts, pageInfo } = await floggy.posts.list({ page: 1, perPage: 10 });
for (const post of posts) {
console.log(post.title, "/" + post.slug);
}
// Post page (rendered HTML + adjacent nav + SEO block + JSON-LD)
const post = await floggy.posts.get("hello-world", { format: "html" });
console.log(post.rendered); // ready-to-inject HTML string
console.log(post.adjacent.prev); // { slug, title } | null
const metadata = floggy.seo.meta(post); // plain Next.js-compatible Metadata object
Writing
With a key that carries posts:write (and posts:read to preview), the same client creates, previews, and publishes.
const floggy = createClient({ project: "projecta", key: process.env.FLOGGY_KEY });
// Markdown in, draft out. Nothing is live yet.
const { id } = await floggy.posts.create({
title: "Launch Day",
content: "## What changed\n\nWe shipped the new **editor**.",
});
// Preview by slug: unpublished posts only, so a published slug returns null.
const draft = await floggy.posts.draft("launch-day");
// Ship it.
await floggy.posts.publish(id);
Every authenticated call acts on the key owner's blog. project only selects the blog the public reads come from.
Configuration
createClient({
project: "projecta", // required: the Floggy username/project
key: process.env.FLOGGY_KEY, // optional: flg_ key for drafts/private content
site: { // optional: used by feeds.*
url: "blog.example.com", // public origin (protocol auto-added)
name: "Example", // defaults to project name
description: "My blog", // defaults to "<name>'s blog"
locale: "en", // defaults to "en"
},
fetch: customFetch, // optional: custom fetch implementation
});
Auth
Public reads need no key. Pass an flg_ key to read drafts and private content (posts:read), to create, update, and publish (posts:write), or to delete (posts:delete). Use a Read-only key in any frontend - a leaked read key only exposes content that is already public. Never embed an Editor or Full-access key in a frontend: it can publish and delete your posts.
const floggy = createClient({ project: "projecta", key: process.env.FLOGGY_KEY });
Methods at a glance
The client exposes five namespaces - posts, tags, seo, feeds, collections:
posts(public) -list,get,searchposts(your own, needs a key) -mine,getById,draft,create,update,publish,unpublish,delete,bulktags-listseo-meta,jsonLdfeeds-sitemap,rss,robotscollections- versioned, schema-driven content stores
Standalone exports too: markdownToTiptap, tiptapToMarkdown, and slugify.
See the SDK reference for full signatures, options, and return shapes.
Errors
Every non-2xx response throws a typed error.
import { FloggyError, RateLimitError, ScopeError } from "@floggy/cms";
try {
await floggy.posts.publish(id);
} catch (err) {
if (err instanceof ScopeError) {
console.log("this key needs:", err.required.join(", "));
} else if (err instanceof RateLimitError) {
console.log("retry after", err.retryAfter, "seconds");
} else if (err instanceof FloggyError) {
console.log(err.status, err.message, err.code);
}
}
FloggyError carries status, message, code, and url. ScopeError (status 403) adds required, the scopes the endpoint asked for. RateLimitError (status 429) adds retryAfter (seconds, parsed from the Retry-After header).
Next
- Quickstart - list, fetch, render, and add SEO in about 15 lines.
- SDK reference - every
@floggy/cmsmethod, reads and writes, with Next.js and TanStack Start examples. - API reference - the REST endpoints underneath, for curl and non-SDK use.
- Authentication - creating
flg_keys, the scope table, presets, and key-safety rules. - Webhooks - subscribe to
post.published/post.updatedand verify signatures. - Collections - headless, schema-driven content stores with versioning and A/B labels, driven through
floggy.collections.*.