Dyrecteddyrected
API Reference

SDK Reference

Use the framework-agnostic @dyrected/sdk client safely and typefully.

@dyrected/sdk is the framework-agnostic client for Dyrected's REST API. It works in server runtimes and browsers with fetch. The SDK covers documents, globals, auth, preferences, preview data, uploads, and workflows; database-specific aggregation belongs behind an application endpoint.

Installation

pnpm add @dyrected/sdk

Creating a client

Pass the origin or mount base before /api; SDK methods append /api/... themselves.

import { createClient } from '@dyrected/sdk'

export const client = createClient({
  baseUrl: 'https://example.com',
  apiKey: process.env.DYRECTED_API_KEY,
  siteId: process.env.DYRECTED_SITE_ID,
})

Do not expose a privileged site API key in browser bundles. Use public collection access, a user JWT, or a trusted server boundary as appropriate.

defaultDepth defaults to 1. A custom fetch is useful for framework instrumentation, tests, or runtimes that provide their own request implementation.

Collection methods

Access a collection through client.collection(slug), never client.collections.

Finding documents

const result = await client.collection('posts').find({
  limit: 10,
  page: 1,
  sort: '-createdAt',
  depth: 1,
  where: { status: { equals: 'published' } },
})

Use findOne(id) when the ID is known. Use depth: 0 for lightweight lists and increase it only when the view needs hydrated relationships.

Common filter operators include equals, not_equals, in, not_in, exists, gt, gte, lt, lte, contains, and starts_with. Multiple sibling field conditions are combined with AND.

Auto-Seeding and Fallbacks (initialData)

When retrieving collections or individual documents, you can pass initialData to provide immediate fallback content (e.g., loaded from a local JSON file) if the database is empty or the document does not exist:

import blogFallback from './blog-content.json'

const result = await client.collection('articles').find({
  sort: '-date',
  initialData: blogFallback.articles,
})
  • Instant Rendering: The SDK immediately returns the fallback data to the caller so the application renders without delay.
  • Background Auto-Seed: In the background, the SDK automatically calls the database seed endpoint to save the fallback data into the CMS database, making it available for editing in the Admin UI.

Creating, updating, and deleting

const post = await client.collection('posts').create({
  title: 'A new post',
  status: 'draft',
})

await client.collection('posts').update(post.id, { status: 'published' })
await client.collection('posts').delete(post.id)

Updates are partial. Access rules, hooks, validation, and workflows still run on the server.

deleteMany(ids) is available for authorized bulk deletion. Treat it as a destructive operation and confirm intent in application UI.

Uploading files

const media = await client.collection('media').upload(file, {
  alt: 'Mountain at sunrise',
})

The collection must be upload-enabled. In browsers, pass a File or Blob; keep provider credentials on the server.

Workflow methods

const updated = await client.collection('posts').transition(
  'post-id',
  'publish',
  {
    expectedRevision: 2,
    comment: 'Approved after legal review.',
  },
)

const history = await client.collection('posts').workflowHistory('post-id', {
  limit: 20,
})

Use expectedRevision to detect concurrent edits. Available transitions and required comments come from the collection workflow; do not hard-code permissions from button visibility.

Globals

const settings = await client.global('site-settings').get({ depth: 1 })
await client.global('site-settings').update({ siteName: 'Example' })

Globals are singleton values and do not have collection pagination.

Like collections, globals support the initialData fallback parameter. If the global has not been configured or is empty, the SDK immediately returns the fallback data and triggers a background seed operation to save it to the database:

const settings = await client.global('site-settings').get({
  initialData: {
    siteName: 'My Default Site',
    maintenanceMode: false,
  }
})

Authentication

Authentication methods live on an auth-enabled collection:

const { token, user } = await client
  .collection('users')
  .login('[email protected]', password)

client.setToken(token)
const currentUser = await client.collection('users').me()

The fluent collection client also exposes logout, token refresh, initialization checks, first-user registration, invitations, invitation acceptance, password changes, reset-link requests, and password reset. These methods are meaningful only for auth: true collections.

Call clearToken() after logout. getAuthHeaders() returns the supported authentication headers when a raw request must share the SDK's current credentials.

Schemas, preferences, and preview data

  • getSchemas() returns serialized collection and global schemas.
  • getPreference(key) and setPreference(key, value) store authenticated user preferences.
  • getPreviewData(token) resolves signed preview data.

Preview tokens are credentials. Do not log them or place them in long-lived analytics URLs.

TypeScript schema inference

Use InferSchema with exported collection/global constants so response types follow the configuration rather than manually duplicated interfaces.

import { createClient, type InferSchema } from '@dyrected/sdk'
import type { Posts, SiteSettings } from './dyrected.config'

type Schema = InferSchema<
  { posts: typeof Posts },
  { 'site-settings': typeof SiteSettings }
>

const typedClient = createClient<Schema>({ baseUrl: 'https://example.com' })
const { docs } = await typedClient.collection('posts').find()

Error handling

Non-success responses throw DyrectedError.

import { DyrectedError } from '@dyrected/sdk'

try {
  await client.collection('posts').findOne('missing-id')
} catch (error) {
  if (error instanceof DyrectedError) {
    console.error(error.statusCode, error.message, error.errors)
  } else {
    throw error
  }
}

Handle authorization and validation failures explicitly; do not turn every failure into an empty successful state. Use initialData only when a deliberate seed/fallback behavior is desired.

Framework usage

Create server-scoped clients when credentials differ per request. Avoid a process-global mutable token in multi-user server code. See the Next.js, Nuxt, and SDK integration guides.

Generated SDK contracts

Only public exported contracts and public class members belong in this region.

BaseSchema

Exported interface from @dyrected/sdk.

export interface BaseSchema {
  collections: Record<string, UnknownRecord>;
  globals: Record<string, UnknownRecord>;
}
MemberSignatureDescription
collectionscollections: Record<string, UnknownRecord>
globalsglobals: Record<string, UnknownRecord>

createClient

Exported function from @dyrected/sdk.

export function createClient<TSchema extends BaseSchema = BaseSchema>(
  config: DyrectedClientConfig,
): DyrectedClient<TSchema>

DyrectedClient

Exported class from @dyrected/sdk.

export class DyrectedClient<TSchema extends BaseSchema = BaseSchema> {
}
MemberSignatureDescription
setTokensetToken(token: string): voidUpdate the Authorization header with a Bearer token. Call this after a successful login.
clearTokenclearToken(): voidRemove the Authorization header. Call this after logout.
getAuthHeadersgetAuthHeaders(): Record<string, string>Returns the headers needed to authenticate raw `fetch()` calls made outside the SDK client (e.g. streaming endpoints, dynamic options). Includes the Authorization bearer token (if set), x-api-key, and x-site-id.
getBaseUrlgetBaseUrl()
getSchemasgetSchemas(): Promise<SchemaResponse>
getAdminAuthConfiggetAdminAuthConfig(): Promise<PublicAdminAuthConfig>
exchangeAdminAuthexchangeAdminAuth(providerId: string, body: Record<string, unknown>): Promise<{ token: string; collectionSlug: string; providerId: string }>
getPreferencegetPreference<T = unknown>(key: string, options?: { scope?: "personal" | "global" }): Promise<{ key: string; value: T | null }>
setPreferencesetPreference<T = unknown>(key: string, value: T, options?: { scope?: "personal" | "global" }): Promise<{ key: string; value: T }>
deletePreferencedeletePreference(key: string, options?: { scope?: "personal" | "global" }): Promise<{ success: boolean }>
getPreviewDatagetPreviewData<T = unknown>(token: string): Promise<T>Fetch draft data for a specific preview token. Used in "token" preview mode.
findfind<K extends keyof TSchema["collections"]>(collection: K & string, args: QueryArgs<TSchema["collections"][K]> = {}): Promise<PaginatedResult<TSchema["collections"][K]>>
collectioncollection<K extends keyof TSchema["collections"]>(slug: K & string)Returns a fluent query builder for a collection.
globalglobal<K extends keyof TSchema["globals"]>(slug: K & string)Access a global by its slug with a fluent builder.
findOnefindOne<T = UnknownRecord>(collection: string, id: string, args: { depth?: number; initialData?: T } = {}): Promise<T>
createcreate<T = UnknownRecord>(collection: string, data: Partial<T>): Promise<T>
updateupdate<T = UnknownRecord>(collection: string, id: string, data: Partial<T>): Promise<T>
deletedelete(collection: string, id: string): Promise<{ message: string }>
transitiontransition<T = WorkflowDocument>(collection: string, id: string, transitionName: string, opts: TransitionOptions = {}): Promise<T>Perform a workflow transition on a document. Sends `POST /api/collections/:collection/:id/transitions/:transition`. Requires the client to have a valid bearer token set via `setToken()`.
workflowHistoryworkflowHistory(collection: string, id: string, args: { limit?: number } = {}): Promise<PaginatedResult<WorkflowHistoryEntry>>Fetch the workflow history for a document. Sends `GET /api/collections/:collection/:id/workflow-history`.
deleteManydeleteMany(collection: string, ids: string[]): Promise<{ message: string }>
getGlobalgetGlobal<T = UnknownRecord>(slug: string, args: { depth?: number; initialData?: T } = {}): Promise<T>
updateGlobalupdateGlobal<T = UnknownRecord>(slug: string, data: Partial<T>): Promise<T>
listMedialistMedia(args: QueryArgs<Media> = {}, collection: string = "media"): Promise<PaginatedResult<Media>>
uploadMediauploadMedia(file: File, collection: string = "media"): Promise<Media>
deleteMediadeleteMedia(id: string, collection: string = "media"): Promise<{ message: string }>

DyrectedClientConfig

Exported interface from @dyrected/sdk.

export interface DyrectedClientConfig {
  baseUrl: string;
  apiKey?: string;
  siteId?: string;
  headers?: Record<string, string>;
  fetch?: typeof fetch;
  /** Default depth for relationship population. Applied to every request unless overridden per-call. */
  defaultDepth?: number;
}
MemberSignatureDescription
baseUrlbaseUrl: string
apiKeyapiKey?: string
siteIdsiteId?: string
headersheaders?: Record<string, string>
fetchfetch?: typeof fetch
defaultDepthdefaultDepth?: numberDefault depth for relationship population. Applied to every request unless overridden per-call.

DyrectedError

Structured error thrown by the SDK when the server returns a non-2xx response.

export class DyrectedError extends Error {
}
MemberSignatureDescription
statusCodereadonly statusCode: number
errorsreadonly errors: { field?: string; message: string }[]

InferSchema

Derives a typed TSchema from your exported collection and global config constants.

Pass it to createClient<Schema>() so every find, findOne, create, update, global().get() call returns the inferred document shape — no manual interfaces required.

export type InferSchema<
  TCollections extends Record<string, CollectionConfig<UnknownRecord>>,
  TGlobals extends Record<string, GlobalConfig<UnknownRecord>> = Record<never, never>,
> = {
  collections: { [K in keyof TCollections]: ExtractDoc<TCollections[K]> };
  globals: { [K in keyof TGlobals]: ExtractDoc<TGlobals[K]> };
};

TransitionOptions

Options accepted by client.transition().

export interface TransitionOptions {
  /**
   * The revision number currently shown to the user. When provided, the server
   * rejects the transition if the document has changed since it was loaded,
   * preventing lost-update races.
   */
  expectedRevision?: number;
  /** Required for transitions that have `requireComment: true` (e.g. `reject`). */
  comment?: string;
}
MemberSignatureDescription
expectedRevisionexpectedRevision?: numberThe revision number currently shown to the user. When provided, the server rejects the transition if the document has changed since it was loaded, preventing lost-update races.
commentcomment?: stringRequired for transitions that have `requireComment: true` (e.g. `reject`).

UploadOptions

Options for file uploads. When onProgress is provided and the runtime supports XMLHttpRequest (browsers), the upload reports real byte-level progress. In other environments (SSR, custom fetch) the callback is ignored and the standard fetch path is used.

export interface UploadOptions {
  /** Called with an integer 0–100 as the file bytes are sent. */
  onProgress?: (percent: number) => void;
  /** Abort the in-flight upload. */
  signal?: AbortSignal;
}
MemberSignatureDescription
onProgressonProgress?: (percent: number) => voidCalled with an integer 0–100 as the file bytes are sent.
signalsignal?: AbortSignalAbort the in-flight upload.

WorkflowDocument

Shape of a document returned from a workflow-enabled collection.

export interface WorkflowDocument {
  id: string;
  _workflow: WorkflowMetadata;
  [key: string]: unknown;
}
MemberSignatureDescription
idid: string
_workflow_workflow: WorkflowMetadata

WorkflowHistoryEntry

A single workflow history entry returned by client.workflowHistory().

export interface WorkflowHistoryEntry {
  id: string;
  collection: string;
  documentId: string;
  transition: string;
  from: string;
  to: string;
  revision: number;
  comment: string | null;
  actorId: string | null;
  createdAt: string;
}
MemberSignatureDescription
idid: string
collectioncollection: string
documentIddocumentId: string
transitiontransition: string
fromfrom: string
toto: string
revisionrevision: number
commentcomment: string | null
actorIdactorId: string | null
createdAtcreatedAt: string

On this page