GraphQL API

DynapiCMS generates a GraphQL API from your schema: every content type gets queries (read), mutations (create, update, delete), filtering, and sorting. No CRUD to write.

Endpoint: POST /cms/graphql. Playground: GET /cms/playground.

Authentication

Every API request requires a token or API key:

Method Header
JWT Authorization: Bearer <jwt>
JWT (alias) Authorization: Token <jwt>
API key X-API-Key: <key>
Cookie auth_token (from admin login)

The API key is also accepted as a query parameter: ?api_key=<key>.

GraphQL naming

The type’s slug sets the API names. Slug blog-post produces:

Name Purpose
blogPost(id, slug) Single record
blogPosts(where, orderBy, limit, offset) List
blogPostsCount(where) Count
createBlogPost(input) Create
updateBlogPost(id, input) Update
deleteBlogPost(id) Delete

Reading a single record

query {
  blogPost(slug: "hello-world") {
    id title status
    author { id name }
  }
}

The slug argument is only available when the type has a slug field marked Unique. Otherwise, use id:

query {
  blogPost(id: "550e8400-e29b-41d4-a716-446655440000") {
    title
  }
}

Localized fields return the value for the locale argument or the X-Locale header:

query {
  blogPost(slug: "hello", locale: "ru") {
    title
  }
}

Reading a list

query {
  blogPosts(limit: 10, offset: 0) {
    id title status
  }
}

limit defaults to 10, max 100. offset is for pagination.

The list accepts where (filter), orderBy (sort), search (text search), and a separate blogPostsCount(where) for the total count. The full operator reference, combinators, and sorting details are on the Filtering & sorting page.

Example with filter and sort:

query {
  blogPosts(
    where: { status: { eq: "published" } }
    orderBy: [{ field: PUBLISHED_AT, direction: DESC }]
  ) { id title }
}

Creating a record

mutation {
  createBlogPost(input: {
    title: "New post"
    slug: "new-post"
    status: "draft"
  }) {
    id title slug
  }
}

Required fields must be filled. References are passed by ID:

mutation {
  createBlogPost(input: {
    title: "With author"
    author: "550e8400-e29b-41d4-a716-446655440000"
  }) { id }
}

Localized fields are passed as a locale map:

mutation {
  createBlogPost(input: {
    title: { ru: "Привет", en: "Hello" }
  }) { id }
}

Updating a record

Partial update — only the supplied fields change:

mutation {
  updateBlogPost(
    id: "550e8400-e29b-41d4-a716-446655440000"
    input: { status: "published" }
  ) { id status }
}

Deleting a record

mutation {
  deleteBlogPost(id: "550e8400-e29b-41d4-a716-446655440000")
}

Returns true on success.

Uploading media

A dedicated mutation for files (multipart upload):

mutation($file: Upload!) {
  uploadMedia(file: $file) { id name url mimeType fileSize type }
}

The file is sent as multipart form data. Returns a ready media record with ID and URL.

What’s next