KeystoneJS Review and Features

Written by Ashita Gopalakrishnan

Last update: 6/11/2024

KeystoneJS is an open-sourced framework that is more than just a headless CMS, it works as a platform for next-gen seamless workflows. With an ideal backend, KeystoneJS works like a charm to help front-end developers focus on UI development with an accessible GraphQL API.

KeystoneJS has 5k stars and 609 fork requests on GitHub, and is (one of) the most powerful headless CMS available today. Mostly because of how fast you can build and scale your applications compared to other CMS/Application frameworks. A product of Thinkmill design consultancy that has worked with well-known clients such as Atlassian, Samsung and Qantas to name a few, Keystone is an ideal choice in various scenarios: from creating a simple blog to handling a whole ecommerce website.

What is KeystoneJS?

An API-driven self-hosted CMS, KeystoneJS is an open-sourced, powerful headless Content Management System(CMS) platform used to build scalable data-driven applications.

Built using Node.js and with more than a million npm installs, Keystone has proven to be suitable for both medium-sized and large-scale companies dealing with complex applications that require scalability.

How does KeystoneJS work?

Following an MVT (Model-View-Template) design pattern, KeystoneJS allows you to outline your schema by providing field types that are required for your needs. Keystone supports the use of primitive data types such as Text, Password, DateTime and Relationship to name a few. In addition to these field types , Keystone also supports complex types such as OEmbed, CloudinaryImage and Markdown that come separately with its own package.

With this schema, you can:

  • add logic in your application, which gives you the power to control how you perform authorization, authentication and testing.
  • create queries wherein Keystone provides you with a GraphQL accordingly for query filtering.
  • perform mutations needed for your application such as create, read, update and delete. These mutations can be applied to single or multiple items in the list.

Developers can query their data by accessing the GraphQL API which allows them to perform CRUD operations, session management as well as other features such as pagination and sorting. For content management, KeystoneJS provides an Admin UI that gives you an understanding of access control and how to implement it according to the schema.

KeystoneJS User Interface

The KeystoneJS interface is pretty straightforward. Once the server is connected, you will be prompted to a default homepage that contains the Header at the center as well as the Admin UI button.

The Admin UI displays a hamburger icon on the left and a defined list on the right.The navigation menu contains a link to the GraphQL playground, GitHub repository as well as the dashboard.

Database and Project Setup

You will need a database to store your application data. You could use either MongoDB or PostgreSQL among these two as your preferred choice of database. You could also use Prisma although it is an experimental option on version5 of Keystone. Once keystone is installed, you will be asked the following questions:

  • What is your project name? - Add the name of your project.
  • Select a starter project. - Select blank.
  • Select a database type. - Choose the database of your choice.
  • Where is your database located? - Provide a connection string.
  • Test your database connection - Test to see if Keystone can connect to the database.

After completing the above procedures, start the server by running the yarn dev command.


Lists are schemas which are composed of fields that each have a type. Fields are objects wherein the field name is the key and the value is the object of a corresponding field type. For example, consider a Blog which has two lists: Post and User. Each of these lists has a fields object with its type and related options.

const { Text, Select, Relationship, DateTime } = require('@keystonejs/fields')

const postFields = {
  fields: {
    title: {
      type: Text,
      isRequired: true
    body: {
      type: Text,
      isMultiline: true
    status: {
      type: Select,
      options: 'PUBLISHED, UNPUBLISHED',
      defaultValue: 'PUBLISHED'

    author: {
      type: Relationship,
      ref: 'User',
      many: false,
      isRequired: true
    publishedTime: {
      type: DateTime,
      format: 'dd/MM/yyyy HH:mm O',
      yearPickerType: 'auto'

module.exports = postFields

The Post List contains the title, body, status of the post, author as well as the published time.

The User List contains the following fields: name, email id, password and isAdmin to separate the Admin from the rest of the Users.

const { Text, Password, Checkbox } = require('@keystonejs/fields')

const userFields = {
  fields: {
    name: {
      type: Text,
      isRequired: true
    email: {
      type: Text,
      isRequired: true,
      isUnique: true
    password: {
      type: Password,
      isRequired: true

    isAdmin: {
      type: Checkbox,
      isRequired: true

Once a basic schema is defined in the list, you need to import it into the index.js file by creating a new instance.

const PostSchema = require('./lists/Post')
const UserSchema = require('./lists/User')

keystone.createList('User', UserSchema)
keystone.createList('Post', PostSchema)

Defining relationships in KeystoneJS

To establish a connection between the contents of different lists, Keystone provides a relationship field type within the list. Cardinality helps to understand whether one or many items are related. Relationships are divided into two types:


  • One-to-many
  • Many-to-many


  • One-to-one
  • One-to-many
  • Many-to-many

An example of one-to-one cardinality is shown below where one post can have only one user.

GraphQL API for CRUD operations

Keystone provides a GraphQL API where you can query your data as well perform mutations. To perform these operations, GraphQL provides a GraphQL playground where you can work around with different queries.

Through Keystone, you can generate queries and mutations such as:

  • allPosts
  • _allPostsMeta
  • Post
  • _PostsMeta
  • createPost
  • updatePost
  • deletePost

The allPosts query fetches all the items in the Post list.

The _allPostsMeta fetches the meta information of the Post list such as count of all the items on the list.

The Post query returns information about a single item from the Post list such as title, body, author, etc.

The _PostsMeta query retrieves information of the meta data and its sub-types such as key, path, access, schema, etc.

The createPost mutation is used to add a new post in the Post list.

deletePost mutation deletes post from the Post list.

Authentication and Access control

An application would be considered incomplete without the required authentication and authorization.

For User Authentication to be set up, the authStrategy needs to be instantiated below the created list.

const authStrategy = keystone.createAuthStrategy({
  type: PasswordAuthStrategy,
  list: 'User',
  config: {
    identityField: 'username',
    secretField: 'password'

Access control refers to a set of actions that can be taken up by an authenticated user. With the access option, you can restrict or allow users access to CRUD operations.

For example, the User list can be modified or updated only by an Admin.

access: {
  read: true,
  create: isAdmin,
  update: isAdmin,
  delete: isAdmin

What’s new in KeystoneJS v6?

The latest version of Keystone comes with added features that help in building and scaling our applications quickly. These features include:

  • A new CLI (Command line interface).
  • Database migrations.
  • Customised Admin UI and logo.
  • Design schema with TypeScript.
  • An improved Access Control API where you can build custom roles according to the schema.
  • The newly added document field type to easily edit content in the system.
  • Build your application without the need for boilerplate code.
  • The optional /healthcheck endpoint informs the server if the Keystone instance is running or not.

KeystoneJS Support

KeystoneJS has an active presence on Twitter including a personalized Slack channel that is dedicated to updating developers about their latest features and releases as well as addressing any issues faced by users . While KeystoneJS is a product of Thinkmill, it has a thriving and inclusive community of developers who contribute to this project by adding their inputs and suggestions.

Keystone also has project examples available on GitHub for you to have a better understanding of the system which is beneficial for beginners who want to try it in development mode before moving into production.

KeystoneJS Pricing

One of the highlights of using KeystoneJS is that it is an open-source project that is completely free to use forever with no lock-in period, giving you the sole authority to deploy your application on the server of your choice.


KeystoneJS has come a long way since it was first introduced in 2013.

Until the first half of 2021, the most stable version that was used was version 5 of Keystone since it was consistent and tested for a long time. However, version 5 of Keystone did not have complete documentation which made it hard to understand.

Currently, v5 has been shifted to maintenance mode. This means that although this version can be used for production, it is highly unlikely for developers to see added changes to this version when v6 has much more to offer. Keystone documentation clearly mentions using v6 for further projects.

Two major aspects one needs to consider while moving to v6 is the configuration which requires some time in rewriting and removal of database adapters, with the support of only Postgres as its database back-end.

The current version has a lot of added features that make it look promising. While you can expect minor changes in the latest version, KeystoneJS is an undeniably powerful headless content management system worth trying out since the team at Keystone is constantly developing new features while maintaining its core vision: to provide an instant GraphQL API and a manageable UI based on your schema. All this and much more while being completely free of cost.

[@portabletext/react] Unknown block type "prosCons", specify a component for it in the `components.types` prop