Dokumentasi Resmi
|tulis.org

Arsitektur Dokumentasi

Peta jalan panduan, SDK integration, dan API reference lengkap yang disiapkan secara terstruktur.

TULIS CMS/DEVELOPER HUB
/sdks-javascript-fetch

API Publik Tulis — Contoh JavaScript (fetch)

Klien kecil yang dapat digunakan kembali yang dibangun di atas API fetch native untuk API publik Tulis headless CMS.

Peringatan keamanan: Simpan kunci API di sisi server. Jangan pernah mengirim apiKey ke browser. Header Authorization: Bearer hanya boleh dilampirkan di lingkungan server tepercaya (Node.js, fungsi serverless, dll.).

Pembantu klien

type TulisClientOptions = {
  apiKey: ***
  workspaceId: string;
  host: string;
};

function createTulisClient({ apiKey, workspaceId, host }: TulisClientOptions) {
  const base = `${host.replace(/\/$/, "")}/v1`;

  async function request<T>(path: string, init: RequestInit = {}): Promise<T> {
    const res = await fetch(`${base}${path}`, {
      ...init,
      headers: {
        "Authorization": `Bearer ${apiKey}`,
        "X-Workspace-ID": workspaceId,
        ...(init.headers ?? {}),
      },
    });

    if (!res.ok) {
      throw new Error(`Tulis API error ${res.status}: ${res.statusText}`);
    }

    return (await res.json()) as T;
  }

  return {
    listPosts(opts: { page?: number; perPage?: number } = {}) {
      const params = new URLSearchParams();
      if (opts.page) params.set("page", String(opts.page));
      if (opts.perPage) params.set("per_page", String(opts.perPage));
      const qs = params.toString();
      return request<{
        status: string;
        message: string;
        data: any[];
        meta: { Page: number; PerPage: number; Total: number; TotalPages: number; HasNext: boolean; HasPrev: boolean };
      }>(`/posts${qs ? `?${qs}` : ""}`);
    },

    getPost(slugOrId: string) {
      return request<{ status: string; message: string; data: any; json_ld: object }>(
        `/posts/${encodeURIComponent(slugOrId)}`
      );
    },

    listTaxonomies(type: "category" | "tag") {
      return request<{ status: string; message: string; data: any[] }>(
        `/taxonomies?type=${encodeURIComponent(type)}`
      );
    },

    listComments(postId: string) {
      return request<{ status: string; message: string; data: any[] }>(
        `/posts/${encodeURIComponent(postId)}/comments`
      );
    },

    createComment(payload: { name: string; email: string; post_id: string; content: string }) {
      return request<{ status: string; message: string; data: any }>(`/comments`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(payload),
      });
    },
  };
}

export { createTulisClient };
export type { TulisClientOptions };

Contoh penggunaan — render daftar posting

import { createTulisClient } from "./tulis-client.js";

// PENTING: jalankan ini hanya di server di mana kunci tidak terekspos ke browser.
const tulis = createTulisClient({
  apiKey: process.env.TULIS_API_KEY,
  workspaceId: process.env.TULIS_WORKSPACE_ID,
  host: process.env.TULIS_HOST,
});

async function renderPostList() {
  const { data: posts, meta } = await tulis.listPosts({ page: 1, perPage: 10 });

  for (const post of posts) {
    console.log(`- ${post.title} (${post.slug})`);
  }

  console.log(`Halaman ${meta.Page}/${meta.TotalPages}, total: ${meta.Total}`);
}

renderPostList().catch((err) => console.error(err));

Lini Masa Peluncuran Fitur

Fase 1: Setup Awal & KonfigurasiSELESAI

Docker Compose, pengaturan file environment, dan panduan setup lokal telah lengkap dibuat.

Fase 2: Referensi RESTful APISELESAI

Dokumentasi route endpoint publik, otentikasi JWT, dan kontrol hak akses telah selesai disusun.

Fase 3: Integrasi Frontend & Fitur LanjutSEDANG BERJALAN

Mempersiapkan boilerplate untuk Next.js client, manajemen revisi konten, dan optimasi query.