First content type

A content type is a schema. You define fields and their types, and DynapiCMS generates a GraphQL API from them: queries, mutations, filtering, sorting. No CRUD to write.

Anatomy of a type

A type has a name, a slug, and a list of fields. The slug sets the GraphQL names: blog-post produces blogPost (single record), blogPosts (list), createBlogPost (mutation). The slug is kebab-case, Latin letters.

Each field is described by:

Property Meaning
Name Field name. Becomes the GraphQL field name (title, published).
Type The type: String, Number, Boolean, Text, Select, Date, DateTime, JSON, Content type.
Required The field is required when creating a record.
Make array A list of values (not available for all types).
Localized field Different values per language ({ru, en}).
Entity ref For Content type fields — which type it points to.

Field types

Type GraphQL What it is Array Localized
String String Single-line text yes yes
Text String Markdown with toolbar and preview yes yes
Number Float Number (with fractional part) yes yes
Boolean Boolean Yes/no toggle no yes
Select String Pick from a list of options no yes
Date String Date without time yes yes
DateTime DateTime Date with time yes yes
JSON String Arbitrary JSON no yes
Content type nested object Reference to another content type yes yes*

The Select (select) type is an enum: the value is picked from a fixed set of options. When Select is chosen as the type, an Options editor appears below the field. Each row sets a Value (stored) — what gets saved to the DB (e.g. draft) — and a Label (shown) — what the editor sees (e.g. Draft). A Default value is also picked. The server rejects any value not in the configured option list.

The Text type is a markdown editor (CodeMirror, toolbar, preview). Don’t confuse it with String: String is a plain single-line input.

References between types (Content type)

A Content type field references another type. Create an Author type with a name field (String). Then in Blog Post add an author field (Content type → Author). In GraphQL this is a nested object — you can query the author’s name inside a post:

query {
  blogPosts(limit: 10) {
    title
    author {
      id
      name
    }
  }
}

An array of references (Content type + Make array) stores a list — for example, tags or related posts. Element order in the array is preserved.

Arrays

The Make array checkbox turns a field into a list of values. Available for String, Number, Text, Date, DateTime, Content type. Boolean, Select, and JSON cannot be arrays. In GraphQL the array becomes [String], [Float], etc.

Localized fields

The Localized field checkbox makes a value multilingual. The record stores {ru: "Привет", en: "Hello"}. When queried through the locale argument, the string for the requested language is returned. Available for all types, including Content type references (e.g. the background image in Hero is a localized Content type → Media field).

The only exception: polymorphic blocks (Content type + Make array with no entity_ref, like blocks on Page) cannot be localized — the server rejects it.

Required fields

The Required checkbox checks that a field is filled on create. An empty value (null, missing key, empty array) fails the check. This is a presence check — it does not validate type or format.

Slug as a unique field

The type’s slug is not the same as a slug field inside the type. A slug field can be added manually, marked Required, and used for pretty URLs. To look up a record by slug in GraphQL, the field must be marked Unique — this adds a slug argument to the singular query (blogPost(slug: "hello")).

The Unique flag cannot currently be set through the admin UI — only through a programmatically seeded schema. Built-in types (e.g. Page) already have a slug with Unique.

How the schema changes

Saving a type triggers a GraphQL schema rebuild at runtime. This happens asynchronously: the request that saves the type returns immediately, and the new schema takes effect fractions of a second later. Queries in flight at that moment keep running against the old schema — the swap is atomic, no downtime.

Add a field — it’s in the API. Remove it — it’s gone. No data migration needed: records are stored as JSON, and the schema only describes their shape.

What’s next