JSPM

@cli-blog/node

0.1.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 244
  • Score
    100M100P100Q75685F
  • License MIT

Official zero-dependency Node.js SDK for publishing and delivering content with the Cli Blog API.

Package Exports

  • @cli-blog/node

Readme

@cli-blog/node

Official Node.js client for the Cli Blog API.

Repository: github.com/cli-blog/cli-blog-node

What Is This?

@cli-blog/node is the first-party SDK for publishing and delivering Cli Blog content from trusted Node.js runtimes. It covers the public /v1 content API: posts, authors, media, categories, tags, locales, sitemap XML, feed XML, revisions, and slug redirects.

The package is ESM-first, targets Node.js 20+, uses native fetch, Blob, and FormData, and has no runtime dependencies.

Use it for:

  • Publishing workflows in servers, CI, and agent runtimes.
  • Reading published content for websites and build jobs.
  • Uploading media through native FormData.
  • Managing taxonomy and localized content.
  • Fetching sitemap and feed XML.

The SDK intentionally excludes dashboard-only session, billing, audit, admin, API-key helper, and organization settings routes.

Getting Started

Install the package:

npm install @cli-blog/node

Create a client:

import { CliBlog } from "@cli-blog/node";

const cliBlog = new CliBlog({
  apiKey: process.env.CLI_BLOG_API_KEY!,
});

Use apiUrl for local development or self-hosted environments:

const cliBlog = new CliBlog({
  apiKey: process.env.CLI_BLOG_API_KEY!,
  apiUrl: process.env.CLI_BLOG_API_URL,
});

Use public keys for published-content reads. Use private keys only from trusted servers, CI, CLIs, and agent runtimes. Never expose private keys in browser code.

Reference

Client configuration:

type CliBlogConfig = {
  apiKey: string;
  apiUrl?: string;
  fetch?: typeof fetch;
};

Per-request options:

type RequestOptions = {
  apiKey?: string;
  signal?: AbortSignal;
};

Resources:

Resource Methods
cliBlog.posts list, paginate, get, create, update, publish, schedule, delete
cliBlog.posts.revisions list, get
cliBlog.posts.slugRedirects get
cliBlog.authors list, get, create, update, delete
cliBlog.media list, get, upload, update, delete
cliBlog.categories list, get, create, update, delete
cliBlog.tags list, get, create, update, delete
cliBlog.locales list
cliBlog.sitemap get
cliBlog.feed get

List responses use the API cursor shape:

type ListResponse<T> = {
  object: "list";
  data: T[];
  has_more?: boolean;
  next_cursor?: string | null;
};

The SDK retries safe requests on transient statuses such as 408, 409, 425, 429, and 5xx. Mutating requests are not retried automatically.

Examples

Create and publish a post:

import { CliBlog } from "@cli-blog/node";

const cliBlog = new CliBlog({
  apiKey: process.env.CLI_BLOG_API_KEY!,
});

const author = await cliBlog.authors.create({
  public_name: "Ada Lovelace",
  bio: "Notes from the engine room.",
});

const category = await cliBlog.categories.create({
  name: "Product Updates",
  locale: "en-US",
});

const tag = await cliBlog.tags.create({
  name: "release",
  locale: "en-US",
});

const post = await cliBlog.posts.create({
  title: "First post",
  body_markdown: "# Hello",
  author_profile_ids: [author.id],
  category_ids: [category.id],
  tag_ids: [tag.id],
  seo_title: "First post",
  seo_description: "A launch note published through the SDK.",
});

await cliBlog.posts.publish(post.id, {
  expected_version: post.version,
});

Read published posts:

const posts = await cliBlog.posts.list({
  status: "published",
  fields: ["summary", "seo"],
  include: ["authors", "media"],
  limit: 20,
  locale: "en-US",
});

for (const post of posts.data) {
  console.log(post.title, post.slug);
}

Paginate through posts:

for await (const post of cliBlog.posts.paginate({ status: "published", limit: 50 })) {
  console.log(post.id);
}

Update, schedule, and delete posts:

const updated = await cliBlog.posts.update("first-post", {
  excerpt: "Updated summary",
  expected_version: 2,
});

await cliBlog.posts.schedule(updated.id, "2026-06-18T16:00:00.000Z", {
  expected_version: updated.version,
});

await cliBlog.posts.delete(updated.id);

Upload media:

import { readFile } from "node:fs/promises";

const file = new Blob([await readFile("cover.png")], { type: "image/png" });

const media = await cliBlog.media.upload({
  file,
  filename: "cover.png",
  alt_text: "Cover image",
  caption: "Launch dashboard.",
  metadata: { source: "release-notes" },
});

Attach media to a post:

await cliBlog.posts.update("first-post", {
  featured_media_asset_id: media.id,
});

Work with taxonomy and localization:

const locales = await cliBlog.locales.list();

const spanishCategory = await cliBlog.categories.create({
  name: "Noticias",
  slug: "noticias",
  locale: "es-MX",
});

const tags = await cliBlog.tags.list({
  locale: "es-MX",
  include: ["translations"],
});

console.log(locales.map((locale) => locale.tag), spanishCategory.id, tags.data.length);

Fetch revisions and redirects:

const revisions = await cliBlog.posts.revisions.list("first-post", {
  locale: "en-US",
});

const revision = await cliBlog.posts.revisions.get("first-post", revisions.data[0]!.id);

const redirect = await cliBlog.posts.slugRedirects.get("old-first-post", {
  locale: "en-US",
});

console.log(revision.version, redirect.to_slug);

Fetch sitemap and feed XML:

const sitemapXml = await cliBlog.sitemap.get({ locale: "en-US", limit: 100 });
const feedXml = await cliBlog.feed.get({ locale: "en-US", limit: 20 });

Use request options:

const controller = new AbortController();

const posts = await cliBlog.posts.list(
  { status: "published" },
  {
    apiKey: process.env.CLI_BLOG_PUBLIC_KEY,
    signal: controller.signal,
  },
);

Handle errors:

import { CliBlogError } from "@cli-blog/node";

try {
  await cliBlog.posts.create({ title: "Draft" });
} catch (error) {
  if (error instanceof CliBlogError) {
    console.error(error.code, error.status, error.requestId);
  }
  throw error;
}

Security

  • Never expose private API keys in browser code.
  • Prefer environment variables or secret managers for private keys.
  • Use public keys for published delivery reads.
  • Use private keys for trusted publishing and editorial workflows.
  • The SDK uses native platform APIs and does not add runtime dependencies.