Defining a Content Type
Declare a Content Engine content type once to get a Postgres table, typed CRUD routes, and interactive AdminCP screens.
Content Engine generates a complete CRUD ecosystem from one declaration: a PostgreSQL table, Zod validation schemas, API endpoints, staff permissions, and AdminCP management screens.
Quick start
1. Define the Content Type
import { defineContentType, field } from "@vitnode/core/content"
export const categoryContentType = defineContentType({
id: "example.category",
tableName: "example_categories",
fields: {
name: field.text({ required: true, minLength: 2, maxLength: 100 }),
slug: field.slug({ from: "name", required: true }),
},
})2. Create the Database Model
import { createContentModel } from "@vitnode/core/content/server"
import { categoryContentType } from "@/content/category"
export const categoryContent = createContentModel(categoryContentType)
export const example_categories = categoryContent.table3. Build & Run Migrations
bun run build:plugins && bun run db:migratepnpm build:plugins && pnpm db:migratenpm run build:plugins && npm run db:migrateYour table now exists in PostgreSQL.
Wire into API and AdminCP
1. Register CRUD Routes on the API
Add the model to your plugin's config.api.ts:
import { buildApiPlugin } from "@vitnode/core/api/lib/plugin"
import { categoryContent } from "./database/categories"
export const exampleApiPlugin = () =>
buildApiPlugin({
pluginId: "@vitnode/example",
contentTypes: [
categoryContent,
],
})This immediately registers full CRUD routes under /api/@vitnode/example/admin/content/category.
2. Register Screens in the AdminCP
Connect the definition in src/config.tsx:
import { buildPlugin, contentTypeAdmin } from "@vitnode/core/lib/plugin"
import { FolderIcon } from "lucide-react"
import { categoryContentType } from "./content/category"
export const examplePlugin = () =>
buildPlugin({
pluginId: "@vitnode/example",
contentTypes: [
contentTypeAdmin({
definition: categoryContentType,
icon: <FolderIcon />,
}),
],
})3. Add Translations
Name the record in your locale file using ICU cardinal plural:
{
"@vitnode/example": {
"content": {
"category": {
"label": "{count, plural, one {Category} other {Categories}}",
"title": "Categories",
"desc": "Organize posts into categories."
}
}
}
}4. Open the Management Screen
Visit http://localhost:3000/admin/content/example/category.
You have a complete AdminCP data table with search, sorting, pagination, and modal dialogs to create, edit, and delete records.
defineContentType Options
Prop
Type
Learn More
Content Engine Overview
Declare a content type once in TypeScript and get a Postgres table, Zod schemas, CRUD routes, staff permissions, and AdminCP screens.
Field Reference
Complete guide to adding and configuring field types in Content Engine, including Postgres mappings, Zod validation, and AdminCP controls.