docs/content/docs/walkthroughs/lesson-1.md
Learn how to install Keystone, create your first content type, and get an app up and running with an intuitive editing environment.
Welcome to the Keystone getting started series!
Together we’ll learn how to turn an empty folder into a database-backed Keystone instance with related content types, publishing workflows, password protection, an editing interface, and more.
{% hint kind="tip" %} Looking to demo Keystone in under 5 minutes? Try the quick-start CLI {% /hint %}
This series assumes you have some basic familiarity with:
Let’s start by setting up a workspace for a new Keystone project. Make a new folder and initialise it:
mkdir keystone-learning
cd keystone-learning
npm init
{% hint kind="tip" %}
We’ll be using npm for installing packages, but you can use any other package manager you prefer.
{% /hint %}
Now add Keystone, Prisma, and the SQLite driver adapter:
npm install @keystone-6/core @prisma/adapter-better-sqlite3 @prisma/client better-sqlite3 prisma
Keystone looks for a file named keystone.ts (or keystone.js) at the project root to handle configuration needs. Let's go ahead and set one up using TypeScript:
// keystone.ts
export default {}
And add TypeScript as a dependency:
npm install typescript
{% hint kind="tip" %}
If you’d rather use JavaScript, make a keystone.js file and skip the TypeScript installation step above.
{% /hint %}
Your folder structure should now look like this:
.
├── node_modules # Dependencies
├── keystone.ts # Keystone config
├── package.json # With Keystone and TypeScript as dependencies
├── prisma.config.ts # Prisma CLI and migration configuration
└── package-lock.json # Your npm lock file
Create prisma.config.ts alongside keystone.ts so the Prisma CLI knows where to find the generated schema and database:
import { defineConfig } from 'prisma/config'
export default defineConfig({
schema: 'schema.prisma',
migrations: { path: 'migrations' },
datasource: { url: 'file:./keystone.db' },
})
We now need to configure keystone.ts with two parts to get our project running:
db to define a database configurationlist to define the shape of the information we put in that databaseWe’ll use SQLite in this project to keep things simple, but you can also use Postgres. The minimum config for SQLite looks like this:
import { config } from '@keystone-6/core'
import { PrismaBetterSqlite3 } from '@prisma/adapter-better-sqlite3'
export default config({
db: {
provider: 'sqlite',
prismaClientOptions: () => ({
adapter: new PrismaBetterSqlite3({ url: 'file:./keystone.db' }),
}),
},
lists: {}, // ...
})
The adapter connects Keystone's runtime to the local ./keystone.db database. The matching URL in prisma.config.ts is used by Prisma CLI commands.
{% hint kind="tip" %} Keystone uses Prisma for database access and development-time schema pushes. For production, manage migration files with the Prisma CLI. {% /hint %}
Now that we have a database configured, let’s connect some content to it!
We’re going to build a simple blog with users and posts. Let’s start with the User list using text fields for their name and email:
import { config, list } from '@keystone-6/core';
import { PrismaBetterSqlite3 } from '@prisma/adapter-better-sqlite3';
import { allowAll } from '@keystone-6/core/access';
import { text } from '@keystone-6/core/fields';
export default config({
db: {
provider: 'sqlite',
prismaClientOptions: () => ({
adapter: new PrismaBetterSqlite3({ url: 'file:./keystone.db' }),
}),
},
lists: {
User: list({
access: allowAll,
fields: {
name: text({ validation: { isRequired: true } }),
email: text({ validation: { isRequired: true }, isIndexed: 'unique' }),
},
}),
},
});
List names are derived from the keys in the lists object.
For example, the User key becomes the name for the respective User list.
This key will be used in Admin UI for the list’s default display name, and in Keystone’s auto-generated GraphQL API.
Field names are derived from the keys in the fields object.
Like lists, they’ll be used in Admin UI for field label defaults, and in the GraphQL API. We've added validation to both our fields to say that they
are required, and declared that emails must be unique, so there can only be one user with each email.
{% hint kind="tip" %} Admin UI is the name of Keystone’s user friendly editing environment. Accessible from a browser, it’s the place where we’ll add and update content that will be stored in the database. {% /hint %}
We now have everything we need to start Keystone, so let’s do just that:
npx keystone dev
In a few seconds your terminal will provide you with you a link to the Keystone Admin UI at http://localhost:3000
Head on over to http://localhost:3000/users where you can create your first user with a name and email:
Hurrah! You now have Keystone up and running {% emoji symbol="🎉" alt="Party Popper" /%}
Next up, we’ll level-up our blog starter with a post list and connect it to our users.
{% related-content %} {% well heading="Lesson 2: Relating things" grad="grad1" href="/docs/walkthroughs/lesson-2" target="" %} Connect two content types and learn how to configure the appearance of field inputs {% /well %} {% /related-content %}