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

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors