Skip to content

CloverLabsAI/redrover-blog-example

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Redrover Blog CMS — Usage Guide

This repo is a working Next.js example of how to embed the Redrover blog CMS at /blog on your own site. Clone it, run it, then port the pattern into your app.

The CMS itself lives at https://cms.tryredrover.com and exposes two POST endpoints that return ready-to-render HTML.


How the CMS works

You POST a workspaceId (and optionally a blogId); the CMS responds with a complete HTML page styled for that workspace's appearance settings:

your site (e.g. mysite.com/blog)        cms.tryredrover.com
        |                                       |
        |  POST /listblogs                      |
        |  { "workspaceId": "..." }             |
        | ------------------------------------> |
        |  <!doctype html>... full listing      |
        | <------------------------------------ |
        |                                       |
        |  POST /getblogcontent                 |
        |  { "workspaceId": "...",              |
        |    "blogId": "<slug>" }               |
        | ------------------------------------> |
        |  <!doctype html>... full post page    |
        | <------------------------------------ |

Both endpoints return a complete HTML document (not a fragment). All CSS is inlined; the page is self-contained. You proxy the HTML through to the browser.

Endpoint reference

Endpoint Method Body Returns
/listblogs POST { "workspaceId": "<uuid>" } 200 HTML, 400 if workspaceId missing
/getblogcontent POST { "workspaceId": "<uuid>", "blogId": "<slug>" } 200 HTML, 404 if post not found, 400 on validation

There's no API key — just include the workspaceId in the body. Your workspace ID is in the Redrover dashboard URL.


Run this example

npm install
REDROVER_WORKSPACE_ID=<your-workspace-id> npm run dev

Open http://localhost:3000/blog. The default workspace ID is hardcoded in app/blog/config.ts if you want to skip the env var while exploring.

Two env vars override the defaults:

Var Default What it does
REDROVER_WORKSPACE_ID (hardcoded sample) Which workspace's posts to load
REDROVER_BASE https://cms.tryredrover.com CMS host (override for staging/local CMS)

What's in this repo

app/
├── layout.tsx
├── page.tsx                 marketing-style home with a "View the blog" link
└── blog/
    ├── config.ts            workspace ID + CMS base URL
    ├── lib.ts               fetch helper + link rewriter
    ├── route.ts             GET /blog          → POST /listblogs
    └── [slug]/route.ts      GET /blog/<slug>   → POST /getblogcontent

Five files total. Read them top-to-bottom in that order — it'll take you about two minutes.

The pieces, briefly

config.ts — Two constants. Workspace ID + CMS base URL. Both env-overridable.

lib.ts — A single fetchHtml() helper that POSTs to the CMS, runs the response through rewriteBlogLinks(), and returns the HTML to the caller. Two thin wrappers — fetchBlogList() and fetchBlogPost(slug) — call it with the right body.

route.ts and [slug]/route.ts — Standard Next.js Route Handlers. Each one is two lines: import the helper, return what it gives you.

Why the link rewriter exists

The CMS doesn't know what URL path you've mounted the blog under. So it emits:

  • href="/<slug>" for post links
  • href="/" for the blog "home" link

If you mount the blog at /blog/... (like this example does), those hrefs would navigate to the wrong place. rewriteBlogLinks() does a string replace so they become /blog/<slug> and /blog. The order in the function matters — slug rewrite first, then home — see the comment in lib.ts.

If you mount the blog at the root of your domain (e.g. blog.mysite.com/), you can drop the rewriter entirely.


Drop into your own app (4-step recipe)

Assuming a Next.js App Router project. Adapt as needed for other frameworks — the CMS is just two HTTP endpoints.

1. Add the config

// app/blog/config.ts
export const WORKSPACE_ID = process.env.REDROVER_WORKSPACE_ID!;
export const REDROVER_BASE =
  process.env.REDROVER_BASE ?? "https://cms.tryredrover.com";

2. Add the fetch helper

// app/blog/lib.ts
import { REDROVER_BASE, WORKSPACE_ID } from "./config";

async function fetchHtml(path: string, body: unknown): Promise<Response> {
  const upstream = await fetch(`${REDROVER_BASE}${path}`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body),
    next: { revalidate: 60 }, // cache HTML for 60s — adjust to taste
  });
  const html = rewriteBlogLinks(await upstream.text());
  return new Response(html, {
    status: upstream.status,
    headers: { "Content-Type": "text/html; charset=utf-8" },
  });
}

export const fetchBlogList = () =>
  fetchHtml("/listblogs", { workspaceId: WORKSPACE_ID });

export const fetchBlogPost = (blogId: string) =>
  fetchHtml("/getblogcontent", { workspaceId: WORKSPACE_ID, blogId });

// Rewrite slug links first, then the home link — flipping the order would
// double-prefix `/` -> `/blog` -> `/blog/blog`.
function rewriteBlogLinks(html: string) {
  return html
    .replace(/href="\/([^/"#][^"]*)"/g, 'href="/blog/$1"')
    .replace(/href="\/"/g, 'href="/blog"');
}

3. Add the two routes

// app/blog/route.ts
import { fetchBlogList } from "./lib";
export const GET = () => fetchBlogList();
// app/blog/[slug]/route.ts
import { fetchBlogPost } from "../lib";
export async function GET(_req: Request, { params }: { params: Promise<{ slug: string }> }) {
  const { slug } = await params;
  return fetchBlogPost(slug);
}

4. Set the env var and ship

REDROVER_WORKSPACE_ID=<your-workspace-uuid>

Visit /blog and /blog/<slug>. That's it.


Common questions

Can I get JSON instead of HTML? Not currently — both endpoints return rendered HTML.

How do I customize the rendering (colors, fonts, layout)? In the Redrover dashboard, under your workspace's blog appearance settings. The CMS reads those at request time, so changes go live without a redeploy.

404 on a post that exists. It's almost always because the post is unpublished. The CMS only serves posts where published = true.

The page renders unstyled / broken. Make sure you're returning the response body as Content-Type: text/html, not as a string in JSON. The CMS includes all CSS inline in the HTML — there's nothing extra to load.

Can I serve the blog at a path other than /blog? Yes — change the app/blog/... directory name and update the rewriter's prefix to match (e.g. /articles/$1). If you serve at the domain root, drop the rewriter.

Does it work outside Next.js? Yes. The CMS is just HTTP. Any backend that can fetch and return HTML works — Express, Hono, FastAPI, Rails, Django, plain Lambda, etc. The example happens to use Next.js Route Handlers because they're the most common case.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors