v0.2.0 — Now open source

Schema-as-code
for Contentful

Define content models in TypeScript. Generate versioned migrations. The Drizzle ORM workflow you know — for your CMS.

$ npm install -g @ctkit/cli
View on GitHub

Features

Everything you need

The Contentful workflow, done right.

Schema-as-code

Define content types in TypeScript. Full type safety, autocompletion, and version control.

Auto-generated migrations

Diff local schemas against Contentful. Get timestamped migration files automatically.

Migration tracking

SHA-256 checksums, execution history, and status. Tracked in Contentful itself.

Pull from Contentful

Reverse-engineer existing content types into local TypeScript schemas.

Direct push

Skip migrations during prototyping. Push schemas directly with dry-run support.

Full validation support

Regex, ranges, unique constraints, enums, image dimensions, rich text nodes. All typed.

Workflow

The workflow you already know

If you've used Drizzle Kit, you're already home.

1

Define your schema

Write content types as TypeScript. Get type safety, autocompletion, and version control.

schemas/blogPost.ts
2

Generate a migration

ctkit diffs your local schemas against Contentful and produces a timestamped migration file.

$ ctkit generate
3

Review and apply

Check the generated migration, then apply it. Migration history is tracked automatically.

$ ctkit migrate
4

Verify

Confirm your local schemas match Contentful. Done.

$ ctkit check — All schemas are up to date

Examples

Expressive schema definitions

Every Contentful field type, validation, and property — fully typed.

schemas/blogPost.ts
import {
  ContentTypeSchema,
  FieldType, LinkType, Mark, NodeType,
  validators, richTextValidators,
} from '@ctkit/core';

const blogPost: ContentTypeSchema = {
  id: 'blogPost',
  name: 'Blog Post',
  displayField: 'title',
  fields: [
    {
      id: 'title',
      name: 'Title',
      type: FieldType.Symbol,
      required: true,
      validations: [
        validators.unique(),
        validators.textLength(1, 200),
      ],
    },
    {
      id: 'slug',
      name: 'Slug',
      type: FieldType.Symbol,
      required: true,
      validations: [
        validators.unique(),
        validators.slug(),
      ],
    },
    {
      id: 'body',
      name: 'Body',
      type: FieldType.RichText,
      required: true,
      validations: [
        richTextValidators.allowedMarks([
          Mark.Bold, Mark.Italic, Mark.Code,
        ]),
        richTextValidators.allowedNodeTypes([
          NodeType.Heading2, NodeType.Heading3,
          NodeType.OrderedList, NodeType.UnorderedList,
          NodeType.Blockquote, NodeType.Hyperlink,
        ]),
      ],
    },
    {
      id: 'author',
      name: 'Author',
      type: FieldType.Link,
      linkType: LinkType.Entry,
      required: true,
      validations: [
        { linkContentType: ['author'] },
      ],
    },
  ],
};

export default blogPost;
schemas/product.ts
import {
  ContentTypeSchema,
  FieldType, LinkType, MimeType,
  validators,
} from '@ctkit/core';

const product: ContentTypeSchema = {
  id: 'product',
  name: 'Product',
  description: 'An e-commerce product',
  displayField: 'name',
  fields: [
    {
      id: 'name',
      name: 'Name',
      type: FieldType.Symbol,
      required: true,
    },
    {
      id: 'price',
      name: 'Price',
      type: FieldType.Number,
      required: true,
      validations: [validators.numberRange(0)],
    },
    {
      id: 'sku',
      name: 'SKU',
      type: FieldType.Symbol,
      required: true,
      validations: [validators.unique()],
    },
    {
      id: 'images',
      name: 'Images',
      type: FieldType.Array,
      required: false,
      items: {
        type: FieldType.Link,
        linkType: LinkType.Asset,
        validations: [
          { linkMimetypeGroup: [MimeType.Image] },
        ],
      },
      validations: [validators.arraySize(0, 10)],
    },
    {
      id: 'brand',
      name: 'Brand',
      type: FieldType.Link,
      linkType: LinkType.Entry,
      required: false,
      validations: [
        { linkContentType: ['brand'] },
      ],
    },
  ],
};

export default product;

CLI

Familiar commands

The same command structure as Drizzle Kit.

Command Description
ctkit generateGenerate migration from schema diff
ctkit migrateApply pending migrations
ctkit pushPush schemas directly to Contentful
ctkit checkDiff local schemas vs Contentful
ctkit pullPull content types into local schemas
ctkit statusShow migration status
ctkit dropDelete content types

Get started in seconds

Install, init, and push your first schema.

Getting started
$ npm install -g @ctkit/cli
$ ctkit init
$ ctkit test
Connected to Contentful
$ ctkit push
All schemas pushed successfully