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
- GraphQL queries — singular and plural, variables, locale.
- Content types & fields — all field types in detail.
- Filtering & sorting — Where, operators, OrderBy.