For Developers
A content layer that works the way your codebase does.
Schema in code. Type-safe client. Embedded deployment. No ops overhead. Dyrected fits into the way you already build — without forcing a separate service, a GUI schema builder, or a database you have to babysit.
schema
export default defineConfig({
collections: [
defineCollection({
slug: 'posts',
fields: [
{ name: 'title', type: 'text', required: true },
{ name: 'body', type: 'richText' },
{ name: 'author', type: 'relationship', collection: 'team' },
{ name: 'status', type: 'select',
options: ['draft', 'published'] },
],
access: {
read: () => true,
create: ({ user }) => user?.role === 'editor',
update: ({ user }) => user?.role === 'editor',
delete: ({ user }) => user?.role === 'admin',
},
}),
],
})embedded
// Next.js — app/cms/[...dyrected]/route.ts
import { handler } from '@dyrected/next'
import config from '../../../dyrected.config'
export const { GET, POST, PUT, DELETE } = handler(config)sdk
import { createClient } from '@dyrected/sdk'
const cms = createClient({
baseUrl: process.env.CMS_URL,
apiKey: process.env.API_KEY,
})
// Fully typed — fields, filters, relationships
const posts = await cms.collection('posts').find({
where: { status: { equals: 'published' } },
populate: ['author'],
limit: 10,
})access
access: {
read: () => true,
update: ({ user, doc }) =>
user?.id === doc.author || user?.role === 'admin',
delete: ({ user }) => user?.role === 'admin',
}hooks
hooks: {
afterChange: [
async ({ doc, operation }) => {
if (operation === 'create') {
await notifySlack(
`New post published: ${doc.title}`
)
}
},
],
}auth
defineCollection({
slug: 'customers',
auth: true,
fields: [
{ name: 'name', type: 'text' },
{ name: 'plan', type: 'select',
options: ['free', 'pro'] },
],
})blocks
{ name: 'sections', type: 'blocks',
blocks: ['hero', 'features', 'cta'] }preferences
// Read active preference (personal → global → default)
const pref = await cms.getPreference('list-columns:posts')
// Save a personal preference
await cms.setPreference('list-columns:posts', ['title', 'status', 'publishedAt'])
// Publish a team-wide default (admin role required)
await cms.setPreference('list-columns:posts', ['title', 'status', 'publishedAt'], {
scope: 'global',
})Open-source core.
Full source on GitHub
Full source on GitHub
Self-host for free
Self-host for free
Managed cloud available
Managed cloud available
BSL license
BSL license
No vendor lock-in
No vendor lock-in
A CMS that fits in your repo, not beside it.
Schema in TypeScript. Deployed with your app. Types generated from your config.