Dyrected
ReferenceTypeScript

Generating Types

Generate a Dyrected types file from local config or a running site, and know when that workflow is better than inference alone.

Use this page when you want a real TypeScript file on disk instead of relying only on in-memory inference from your schema constants. By the end, you should know what npx dyrected generate:types reads from, what it writes, how to point it at another config or URL, how to generate into a custom path of your choice, and how to use the generated file with the SDK.

When to use generated types

The best reason to run generate:types is that you want a shareable boundary.

Reach for it when:

  • your frontend should import a generated file instead of importing dyrected.config.ts
  • your schema lives in another package or repo
  • you are generating types from a running Dyrected Cloud or self-hosted instance
  • you want a committed artifact that other tools can consume

If your schema and SDK usage live in one codebase, start with the simpler path from TypeScript Overview: export your schema constants and derive the client type with InferSchema.

The basic command

The default command is:

npx dyrected generate:types

With no extra flags, the CLI reads ./dyrected.config.ts and writes the result into your app's source directory, so your framework's TypeScript program picks it up:

  • Vite (React/Vue) and Next.jssrc/dyrected-types.ts (falls back to the project root if there is no src/)
  • Nuxtapp/dyrected-types.ts

The location matters. The generated file registers your schema through a module augmentation (see Automatic typing), and TypeScript only applies that augmentation when the file is inside the program's include globs. A stray dyrected-types.ts at a Nuxt project root is silently ignored by the type checker — which is why the CLI writes into your source directory. Your dyrected.config.ts stays at the project root; only the generated types file lives in the source dir.

What the command actually does

The current CLI resolves its source in this order:

  1. if you pass --url, it fetches YOUR_URL/api/schemas
  2. otherwise, if the config path exists, it imports the local config file
  3. otherwise, it falls back to http://localhost:3000/api/schemas

That means the same command supports both local-config generation and remote-schema generation. It is not limited to one deployment style.

Generate from a local config

This is the default and simplest path:

npx dyrected generate:types

If your config file lives somewhere else, pass it explicitly:

npx dyrected generate:types --config ./cms/dyrected.config.ts

Use that when your app has a non-standard layout or when the schema lives outside the repository root.

Generate from a running site

If the schema should come from a running self-hosted instance instead of a local file, pass --url.

For a self-hosted site:

npx dyrected generate:types --url http://localhost:3000

This path works by reading the serialized schema from /api/schemas, so it is useful when the running site is the boundary you actually care about.

At the moment, this is the verified remote path for self-hosted instances that expose /api/schemas directly. The current CLI does not have a Dyrected-Cloud-specific remote type-generation flow that accepts siteId and Cloud credentials separately, so this page does not recommend --url as the primary Cloud path yet.

Change the output path

If you do not want the default source-dir location, generate the file anywhere you want:

npx dyrected generate:types --output ./types/cms.ts

This is the usual fit when you keep generated contracts in a dedicated types/ directory, want multiple apps to read from a shared package path, or just prefer a different filename than the default.

If you run npx dyrected init, the CLI adds a helper script to package.json:

{
  "scripts": {
    "dyrected:generate-types": "dyrected generate:types"
  }
}

That gives you a stable rerun command, and you can still add --output whenever you want a non-default destination.

What the generated file contains

The current generator writes a plain TypeScript file with:

  • a built-in Media interface
  • UrlFieldValue and UrlField helper types
  • one interface per collection
  • one interface per global, suffixed with Global
  • a DyrectedSchema interface with collections and globals maps
  • a declare module "@dyrected/sdk" block that registers DyrectedSchema globally, so the SDK client and framework hooks type themselves against your schema automatically (see Automatic typing below)

It also maps common field shapes for you:

  • select and radio fields become string unions when the options are statically known
  • relationship and image fields become RelatedDoc | string
  • blocks become a blockType union
  • upload collections gain upload-specific fields such as filename, mimeType, url, and sizes
  • auth collections gain email and optional roles

The file is generated with Prettier and should be treated as generated output, not a hand-edited source file.

Use the generated schema with the SDK

The generated file includes DyrectedSchema, which is the shape the SDK expects for typed collection and global access.

import { createClient } from "@dyrected/sdk";
import type { DyrectedSchema } from "./dyrected-types";

const client = createClient<DyrectedSchema>({
  baseUrl: process.env.NEXT_PUBLIC_DYRECTED_URL!,
});

const { docs: posts } = await client.collection("posts").find();

That is the easiest way to get typed SDK results when the generated file is your contract boundary.

Automatic typing, no generics needed

The generated file also registers your schema globally with @dyrected/sdk, through a small module augmentation at the bottom of the file:

// dyrected-types.ts (generated — do not edit)
declare module "@dyrected/sdk" {
  interface Register {
    schema: DyrectedSchema;
  }
}

Once that file is part of your project, you no longer have to pass <DyrectedSchema> anywhere. createClient() and every framework hook default to your registered schema on their own:

import { createClient } from "@dyrected/sdk";

// No generic — the client is already typed against DyrectedSchema
const client = createClient({ baseUrl: process.env.NEXT_PUBLIC_DYRECTED_URL! });

const { docs } = await client.collection("posts").find(); // docs: Post[]

The same applies to the React and Vue hooks (and Nuxt, via auto-import). Collection slugs autocomplete and results are typed with no per-call generics:

// Vue
const { docs } = useDyrectedCollection("posts"); // "posts" autocompleted, docs: Post[]
const { data } = useDyrectedGlobal("settings");  // data: Settings

// React
const { client } = useDyrected();
const { docs } = await client.collection("posts").find().exec(); // typed

Registration is global and assumes one schema per app — the same model Payload uses. Until the generated file is part of your compilation, the client and hooks fall back to a loose BaseSchema (slugs are plain strings, documents are Record<string, unknown>). Generating the file is what turns typing on everywhere at once.

You can still pass an explicit generic when you want to override the registered schema for a specific call — for example createClient<OtherSchema>({ ... }) — but you rarely need to.

The recommended path is:

  1. change your schema
  2. rerun npm run dyrected:generate-types or your package manager's equivalent
  3. commit the updated generated file if your project treats it as a shared contract

Do not edit the generated file by hand. Your next generation run will overwrite it.

Escape hatches

The default command is intentionally simple, but you still have room to adjust it:

  • use --config when the schema file is not in the root
  • use --url when the running site is the real source of truth
  • use --output when another package or app should consume the generated file from a different path

If you find yourself needing deeper compile-time control inside the schema code itself, go back to the inference-first path from TypeScript Overview.

On this page

Dyrected| Cloud

Get your backend ready in minutes

Use a managed database, storage, APIs, and admin dashboard without setting up the infrastructure yourself.

Set Up My Backend