PrismDraft SDK

Conventions

The SDK is one object, pd, with one property per API resource and one method per operation. This page explains how a call is shaped, what it returns, and how to reach the layer underneath.

Resources and methods

pd.<resource>.<method>(input, init?). The resource and method come from the operation's id in the API (articles.publish is pd.articles.publish), so the names in the reference are the names in code. Each resource has a page there: articles, categories, webhooks, and so on.

Every method has two exported types, named <Resource><Method>Input and <Resource><Method>Output:

import type { ArticlesListInput, ArticlesListOutput } from "@prism-draft/sdk";

async function publishedTitles(input: ArticlesListInput): Promise<string[]> {
  const page: ArticlesListOutput = await pd.articles.list({ ...input, status: "PUBLISHED" });
  return page.articles.map((article) => article.title);
}

The package also exports paths, components and operations, the types generated from the OpenAPI document, for anything the per-method types do not name.

Article (an item of pd.articles.list), ArticleStatus, Category (the category embedded in it) and ArticleVersion (an item of translations.versions in pd.articles.get) are the common pieces, derived from those types, so they follow the API:

import type { Article, ArticleStatus } from "@prism-draft/sdk";

const isPublished = (article: Article): boolean =>
  article.status === ("PUBLISHED" satisfies ArticleStatus);

Input

A method takes one object. Path parameters and query parameters are flat properties under their own names. The JSON request body goes under body. No operation uses one name for two parameters today, so the properties do not collide. If one ever did, its input would be nested as { path, query, body } and its doc comment would say so.

await pd.articles.list({ workspaceId, categoryId, status: "DRAFT", limit: 50 });
//                       path          query parameters
await pd.articles.update({ articleId, body: { title: "New title" } });
//                         path        request body

When every field of a body is optional, body is optional too. In that case the SDK sends {} if you pass none:

await pd.articles.approve({ articleId }); // sends {}
await pd.articles.approve({ articleId, body: { comment: "Reads well." } });

A method that needs no input (pd.workspaces.list, pd.permissions.list, pd.billing.listPlans, pd.billing.listCreditPackages) takes init as its only argument.

A path parameter that is empty, . or .. throws a TypeError before any request is sent: such a value would rewrite the URL into another route. The API validates everything else, and answers 400 with the code INVALID_REQUEST (see Errors).

init

The second argument carries per-call options:

const controller = new AbortController();
setTimeout(() => controller.abort(), 5_000);

await pd.articles.get({ articleId }, { signal: controller.signal });
await pd.articles.list({ workspaceId }, { timeoutMs: 60_000 });

signal cancels the call. timeoutMs fails an attempt that takes longer; it overrides the client's timeoutMs. See Retries and timeouts.

Output

A method returns the parsed JSON body of the success answer, typed. The status code and headers are not part of it; a non-2xx answer throws instead (see Errors).

  • Dates are ISO 8601 strings, not Date objects.
  • Ids are UUID strings.
  • A nullable field is typed … | null; an optional field may be missing. The reference lists each one.
  • Read the fields you need and ignore the rest: the API adds fields over time (see Versioning).

A body that is an upload takes body and contentType; see Uploads.

Pagination

Lists that can be long take limit and offset and answer one page plus the count of everything that matches:

const page = await pd.articles.list({ workspaceId, limit: 100, offset: 200 });
console.log(page.articles.length, "of", page.total);
Method Items Default limit Largest limit Count
pd.articles.list articles 25 100 total
pd.articleGroups.list groups 25 100 total
pd.dashboard.listAudit entries 30 100 total
pd.blog.listPosts posts 20 50 none

offset starts at 0. pd.articles.list returns the newest change first, so an article edited while you page through can move between pages. Recipes has a loop that walks every page.

pd.raw

pd.raw is the underlying openapi-fetch client, typed over the same paths, with the same authentication, retries, timeouts and error handling. Use it when a method hides something you need, such as the response headers:

const { data, response } = await pd.raw.GET("/api/articles/{articleId}", {
  params: { path: { articleId } },
});
console.log(response.status, response.headers.get("content-type"), data?.article.title);

It returns { data, response }. A non-2xx answer still throws a PrismDraftError, and timeoutMs and signal work per call as they do on a method.