DEV Community

Alex Spinov
Alex Spinov

Posted on

KeystoneJS Has a Free API You've Never Heard Of

KeystoneJS is an open-source headless CMS and GraphQL API framework built by the team at Thinkmill. Define your schema in TypeScript, and Keystone generates a GraphQL API, admin UI, and database migrations automatically.

What Makes KeystoneJS Special?

  • TypeScript-first — schema defined in code
  • Auto-generated GraphQL — complete CRUD API from schema
  • Admin UI — customizable, auto-generated
  • Access control — field-level security
  • Document field — structured rich text with custom blocks

The Hidden API: Schema-Driven GraphQL

import { config, list } from '@keystone-6/core';
import { text, integer, relationship, timestamp, select, image } from '@keystone-6/core/fields';
import { document } from '@keystone-6/fields-document';

export default config({
  db: { provider: 'postgresql', url: process.env.DATABASE_URL },
  lists: {
    Post: list({
      access: { operation: { query: () => true, create: isAdmin, update: isAdmin, delete: isAdmin } },
      fields: {
        title: text({ validation: { isRequired: true } }),
        slug: text({ isIndexed: 'unique' }),
        status: select({
          options: [
            { label: 'Draft', value: 'draft' },
            { label: 'Published', value: 'published' }
          ],
          defaultValue: 'draft'
        }),
        content: document({
          formatting: true,
          dividers: true,
          links: true,
          layouts: [['1fr', '1fr']]
        }),
        author: relationship({ ref: 'User.posts', many: false }),
        tags: relationship({ ref: 'Tag.posts', many: true }),
        publishedAt: timestamp(),
        heroImage: image({ storage: 'local_images' })
      },
      hooks: {
        resolveInput: ({ resolvedData }) => {
          if (resolvedData.title) {
            resolvedData.slug = resolvedData.title.toLowerCase().replace(/\s+/g, '-');
          }
          return resolvedData;
        }
      }
    }),
    Tag: list({
      fields: {
        name: text({ validation: { isRequired: true }, isIndexed: 'unique' }),
        posts: relationship({ ref: 'Post.tags', many: true })
      }
    })
  }
});
Enter fullscreen mode Exit fullscreen mode

This generates:

query {
  posts(where: { status: { equals: "published" } }, orderBy: { publishedAt: desc }, take: 10) {
    id title slug status publishedAt
    author { name }
    tags { name }
    content { document }
  }
}
Enter fullscreen mode Exit fullscreen mode

Quick Start

npm create @keystone-6/app
cd my-app && npm run dev
# Admin: localhost:3000 | GraphQL: localhost:3000/api/graphql
Enter fullscreen mode Exit fullscreen mode

Why Teams Choose Keystone

A developer shared: "We tried Strapi, Directus, and Keystone. Keystone won because the schema IS TypeScript — no clicking through GUIs, no YAML files. Our schema is version-controlled, reviewed in PRs, and fully type-safe."


Building content platforms? Email spinov001@gmail.com or check my toolkit.

What headless CMS do you prefer? Code-first or GUI-first?

Top comments (0)