Keel is a purpose-built backend that heavily accelerates product development by providing powerful tooling to build and manage your product. In this guide, we will setup and use those tools, ultimately deploying our product to Keel.


Before we get started, you'll need to have the following installed:

  1. Docker (opens in a new tab)
  2. Node.js (opens in a new tab)

Optional Prerequisites

Since Keel applications are described in a custom schema language, we have a VSCode extension (opens in a new tab) to get syntax highlighting and autocompletion. If you're using VSCode, we highly recommend installing this extension.


To get the most out of Keel, you'll need the Keel CLI that can be installed by running the following command in your terminal:

npm install -g keel

This CLI will set you up with everything you need to run Keel locally before deploying to production.

Creating a New Project

Great! Now that we have the CLI installed, let's create a new project by creating a directory where we can work. We'll call this directory my-project:

mkdir my-project
cd my-project

Now that we have a directory, we can create a new Keel project by running the following command:

keel init

This will create a few files for us, specifically:

  • schema.keel - The file that represents our application's backend.
  • keelconfig.yaml - Configuration for our backend around secrets, authentication, and more. We'll cover this in more detail later.

Great, we've got a Keel schema. Let's populate it.

The Keel Schema

The Keel schema is a custom language that describes your application's backend. It's a declarative language that allows you to describe your application's data models, actions your application can do on those models, authentication, and more.

Let's explore this by creating a simple application that allows users to create and view posts.

We've created an AI assistant called KeelGPT (opens in a new tab) to help you navigate the Keel schema. It can generate schemas for you, answer questions, and more. It may be helpful as you follow the schema below.

Paste this into your schema.keel file:

// We define a model
model User {
    // A model has fields
    fields {
        identity Identity @unique
        name Text
        email Text
        posts Post[]
    // A model also has actions which can be used via APIs
    actions {
        // Create a new user, setting the identity to the caller of the action
        create createUser() with (name, email) {
            @set(user.identity = ctx.identity)
        // Get a single user
        get getUser(id) 
        // Get a list
        list listUsers()
    // We also must define permissions for our model,
    // otherwise it will be inaccessible by default.
        actions: [get, list, create],
        expression: ctx.isAuthenticated
// Let's repeat this for Posts
model Post {
    fields {
        title Text
        content Text
        author User
    actions {
        create createPost() with (title, content, author.id) {
            @permission(expression: ctx.isAuthenticated)
        get getPost(id)
        list listPosts(author.id?) 
        actions: [get, list],
        expression: ctx.isAuthenticated

From the above snippet, we get our first taste of the Keel schema language. Let's break it down.


Models are the core of your application. They represent the data that your application stores and manipulates. Models have fields that describe attributes about them, and actions that describe what you can do to them.

Models are locked down by default, meaning that you must explicitly define permissions for them to be accessible. We'll cover permissions in more detail later.


Fields are the attributes of your models. They can be of a variety of types, including Text, Number, Timestamp, Boolean, and more. You can also define relationships between models, like User and Post above.


Actions are the operations you can do on your models. They can be create, get, list, update, and delete. You can also define custom actions that can do anything you want. We'll dive into those later in the docs. For now, let's focus on the built-in actions that represent CRUD (Create, Read, Update, Delete) operations.


Permissions are how you control access to your models and actions. They are defined using a custom expression language that allows you to define complex rules around who can access what. We'll cover this in more detail later.

Running Locally

Now that we have a schema, let's run it locally. To do this, we'll need to start the Keel server. We can do this by running the following command:

keel run

You'll need to have Docker running for this to work. You can confirm this by running docker ps in your terminal.

This will start the Keel server locally, which will allow us to interact with our backend. It will give us 3 APIs:

Each API has feature parity, so you can use whichever you prefer. For this guide, we'll use the GraphQL API. You also get a GraphQL API playground at http://localhost:8000/api/graphiql (opens in a new tab).

That's it! Our backend is up and running! Let's interact with it using the GraphQL playground.


Let's start by authenticating with the API so we can create a user. To do this, you can call the token endpoint using the password flow:

curl --request POST \
  --url 'http://localhost:8000/auth/token' \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data grant_type=password \
  --data username='test@keel.xyz' \
  --data password='superSecr3t!'

If all went well, we should receive a response that looks something like this:

  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrZWVsIiwic3ViIjoiMll0ckZPdnFjQmQyZzFKRFQzOE9WQW9qWmMxIiwiZXhwIjoxNzAxNDQyMDEyLCJpYXQiOjE3MDEzNTU2MTJ9.bFBsXXJIaVy4ELRKInBKKQoFEVtAwJ1DUNH9oCwo0aT-5r5lQA6kEXJUrDK35Ini-0rKJfahwz-FcOQ6uLToIxuvoWeTtCLvVjEeBVcJWvpEASxXfntnIvn3oZBOhR8icExbPecbKE0dMmxRxvWmrjr3XDD9gUeWpj1MO4ihCgCszHRFJRjcMdy-3QzQ81LZXn0_SeXVpMgdfKIx7HLR95B2mnQi45isNreg9_lpzpXNIzH6r6YAmgamEmZIBts7e908VI2GeMEgB_ebgr2nN5s9R7X1rOSuXs31dB7LBsJgf2xYJ8KkF8O4SUV3rVZl1smJeBUsuMS6aWvy6cnwkQ",
  "token_type": "Bearer",
  "expires_in": 86400,
  "refresh_token": "Nw6KK5wZ3KwEusohLJoW4ZXi2MMeKSWkOEIiyOjo3p3QbvGmdzi2Amr92OSZANo8",
  "identity_created": false

That access_token is what will let us use the rest of the API.

Interacting with the GraphQL API

Great! Now that we have an Identity we can start querying our API.

Let's navigate to the GraphQL playground by going to http://localhost:8000/api/graphiql(opens (opens in a new tab) in a new tab), and then query the createUser action to create a new User.

To do this, we'll run the following query:

mutation createUser {
    # We're calling the createUser mutation, generated by Keel with the appropriate inputs
  createUser(input: {
    name: "Testy McTesterson", 
    email: "test@keel.xyz"
}) {
    id # We get back an ID

It's important that we're authenticated here, so let's take the access_token from the previous token query and add it to the Headers tab in the GraphQL playground:

  "Authorization": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrZWVsIiwic3ViIjoiMll0ckZPdnFjQmQyZzFKRFQzOE9WQW9qWmMxIiwiZXhwIjoxNzAxNDQyMDEyLCJpYXQiOjE3MDEzNTU2MTJ9.bFBsXXJIaVy4ELRKInBKKQoFEVtAwJ1DUNH9oCwo0aT-5r5lQA6kEXJUrDK35Ini-0rKJfahwz-FcOQ6uLToIxuvoWeTtCLvVjEeBVcJWvpEASxXfntnIvn3oZBOhR8icExbPecbKE0dMmxRxvWmrjr3XDD9gUeWpj1MO4ihCgCszHRFJRjcMdy-3QzQ81LZXn0_SeXVpMgdfKIx7HLR95B2mnQi45isNreg9_lpzpXNIzH6r6YAmgamEmZIBts7e908VI2GeMEgB_ebgr2nN5s9R7X1rOSuXs31dB7LBsJgf2xYJ8KkF8O4SUV3rVZl1smJeBUsuMS6aWvy6cnwkQ"

Great. Now, if we run this mutation, we should receive a response like this:

  "data": {
    "createUser": {
      "id": "2YtrRyJZjwm5KMzY1fYcK3BcYQ7"

We've created a user! The rest of the GraphQL API works similarly, so we won't go into detail here. You can explore the API using the GraphQL playground.

Now, you've got a backend running locally—all that's left is to build a frontend that communicates with this API and we've got a full product.

Deploying to Production

Now that we have a product running locally, let's deploy it to production. To do this, we'll need to create a Keel account. If you don't already have one, you can create one by navigating to the Keel console (opens in a new tab) and signing up.

Then, we create a project on Keel, connect it to a GitHub repository hosting our code, and that's it—we're in production. Our local Keel backend is normally available at http://localhost:8000/api/graphql, but when we deploy to production, it will be available at https://<project-name>.keel.so/api/graphql. We can make customize which version our frontend talks to using environment variables (opens in a new tab) and we're good to go.

This is how Keel drastically accelerates product development by providing powerful tooling and ideal developer experience for product backends end-to-end. We can build our product locally, and when we're ready, we can deploy it to production with a single command. We don't need to worry about infrastructure, databases, or anything else. Keel handles all of that for us, from a single schema.

Next Steps

We've just scratched the surface on models, fields, actions, and permissions. There's a lot more to explore, including custom actions, jobs, and more. You can explore the rest of the docs to learn more about the Keel schema language and how to build powerful backends with it.