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:typesWith 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.js →
src/dyrected-types.ts(falls back to the project root if there is nosrc/) - Nuxt →
app/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:
- if you pass
--url, it fetchesYOUR_URL/api/schemas - otherwise, if the config path exists, it imports the local config file
- 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:typesIf your config file lives somewhere else, pass it explicitly:
npx dyrected generate:types --config ./cms/dyrected.config.tsUse 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:3000This 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.tsThis 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
Mediainterface UrlFieldValueandUrlFieldhelper types- one interface per collection
- one interface per global, suffixed with
Global - a
DyrectedSchemainterface withcollectionsandglobalsmaps - a
declare module "@dyrected/sdk"block that registersDyrectedSchemaglobally, 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:
selectandradiofields become string unions when the options are statically knownrelationshipandimagefields becomeRelatedDoc | stringblocksbecome ablockTypeunion- upload collections gain upload-specific fields such as
filename,mimeType,url, andsizes - auth collections gain
emailand optionalroles
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(); // typedRegistration 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.
Recommended workflow
The recommended path is:
- change your schema
- rerun
npm run dyrected:generate-typesor your package manager's equivalent - 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
--configwhen the schema file is not in the root - use
--urlwhen the running site is the real source of truth - use
--outputwhen 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.