Features Apograph CMS on GitHub

Content plugin

Content types, entries, revisions and publishing — plus the API that serves them.

Documents 0.5.2 Updated Edit this page Report a problem

On this page

The content-modelling plugin. It turns code-defined content types into physical Postgres tables, holds them in a runtime registry, serves that schema to the admin, and validates every value written against it.

Install

npm install @apograph/content-server @apograph/content-admin

Register

// apps/server/src/plugins.ts
import { ContentPlugin } from '@apograph/content-server';

const content = ContentPlugin({
    types: contentTypes,
    migrations: {
        dir: () => join(process.cwd(), 'migrations'),
        table: '__drizzle_migrations_content'
    }
});

Hold it in a variable rather than inlining it: the GraphQL adapter takes the plugin itself, not just its types.

// apps/admin/src/plugins.ts
import { ContentPlugin } from '@apograph/content-admin';

ContentPlugin();

An app with no content types is valid

ContentPlugin({ types: [] }) owns no tables, serves its generic routes against an empty registry, and boots. That is what lets a freshly scaffolded app migrate and run before its first content type exists.

The host owns the tables

This is the one plugin that does not ship migrations. Your content types are yours, so their tables are generated into your app’s own migrations/ directory by apograph generate, and applied by apograph migrate along with everybody else’s.

Pass the migrations descriptor at the point you define your first type. Until then there is nothing to generate.

The DSL

Types are declared in code, in your app:

export const post = collection('post', {
    label: 'Blog posts',
    fields: {
        title: field.text({ required: true, minLength: 3 }),
        author: field.relation({ to: () => author, required: true, onDelete: 'restrict' }),
        tags: field.relation({ to: () => tag, many: true })
    }
});

collection() for a type with many records, single() for a page there is exactly one of. See content types for the full vocabulary and field types for every field.*.

Configuration

It takes no environment variables. Everything about a content model is code — which is the point: the model is reviewable, diffable and deployable, and it cannot drift between environments.

What it serves

The admin API the Content Library drives, and the public content API at /api/v1/… that bearer tokens are spent against. Reading, filtering, expanding, writing and the error shapes are documented under delivering content; the tokens themselves are managed by the API tokens plugin.