KeystoneJS
Welcome to Keystone 5, the development project for the future of KeystoneJS.
schema => ({ api, adminUI })
KeystoneJS is a platform which takes a user defined schema and builds a server which provides a GraphQL CRUD API backed by a database, along with an Admin UI for interacting with the database.
WARNING
This project is currently very much in the alpha
phase of development.
There are known bugs, missing features, and limited documentation.
APIs have not been finalised and may change with each release (although SemVer will always be respected when this happens).
If you use the Knex adapter, KeystoneJS will delete your database every time you restart your application.
Getting Started
Quick start
To get up and running with a basic project template, run the following commands.
yarn create keystone-app my-app
cd my-app
yarn start
Setup
npm install --save @keystone-alpha/keystone @keystone-alpha/fields @keystone-alpha/adapter-mongoose @keystone-alpha/admin-ui
NOTE: You must have a working version of mongo
installed.
Add a script to your package.json
:
{
"scripts": {
"dev": "keystone"
}
}
Create a file index.js
:
const { Keystone } = require('@keystone-alpha/keystone');
const { AdminUI } = require('@keystone-alpha/admin-ui');
const { MongooseAdapter } = require('@keystone-alpha/adapter-mongoose');
const { Text } = require('@keystone-alpha/fields');
const keystone = new Keystone({
name: 'Keystone To-Do List',
adapter: new MongooseAdapter(),
});
keystone.createList('Todo', {
fields: {
name: { type: Text },
},
});
// Setup the optional Admin UI
const admin = new AdminUI(keystone);
module.exports = {
keystone,
admin,
};
Now you have everything you need to run a Keystone instance:
npm run dev
Keystone will automatically detect your index.js
and start the server for you:
http://localhost:3000/admin
: Keystone Admin UIhttp://localhost:3000/admin/api
: generated GraphQL APIhttp://localhost:3000/admin/graphiql
: GraphQL Playground UI
Server Configuration
Extra config can be set with the serverConfig
export in index.js
:
// ...
module.exports = {
keystone,
admin,
serverConfig: {
'cookie secret': 'qwerty',
apiPath: '/admin/api',
graphiqlPath: '/admin/graphiql',
},
};
// TODO: Document _all_ the options
Custom Server
In some circumstances, you may want to do custom processing, or add extra routes the server which handles the API requests.
A custom server is defined in server.js
which will act as the entry point to
your application (in combination with index.js
which defines your schema) and
must handle executing the different parts of Keystone.
Create the server.js
file:
const keystoneServer = require('@keystone-alpha/core');
keystoneServer.prepare({ port: 3000 })
.then(({ server, keystone }) => {
server.app.get('/', (req, res) => {
res.end('Hello world');
});
return server.start();
})
.then(({ port }) => {
console.log(`Listening on port ${port}`);
})
.catch(error => {
console.error(error);
});
Run keystone as you normally would:
npm run dev
Custom Server Configuration
When using a custom server, you should pass the serverConfig
object to the
prepare()
method:
keystone.prepare({
serverConfig: {
/* ... */
},
});
For available options, see Server Configuration.
Production Build
When getting ready to deploy your app to production, there are performance optimisations which Keystone can prepare for you.
Add this script to your package.json
:
{
"scripts": {
"build": "keystone build"
}
}
Run npm run build
to generate the following outputs:
.
└── dist/
├── api/
├── admin/
└── index.js
To run your keystone instance, execute the index.js
file:
cd dist
node index.js
Production Build Artifacts
dist/index.js
An all-in-one server which will start your Keystone API and Admin UI running on the same port.
NOTE: If you've setup a custom server, dist/index.js
will
be a copy of your server.js
dist/api/
The GraphQL API code lives here. This is a combination of your code setting up the keystone instance, and a server to run the API.
This folder contains an index.js
file which when run via node
(node dist/api/index.js
) will serve the API. In this manner, it is possible to
deploy the API independently of the admin UI by deploying the
contents of the dist/api/
folder only.
dist/admin/
A static export of the Admin UI lives here. Built from your code setting up the keystone instance, this export contains list and field config information tightly coupled to the API. It is therefore recommended to always deploy the Admin UI at the same time as deploying the API to avoid any inconsistencies.
Adding Authentication
See Authentication docs.
To setup authentication, you must instantiate an Auth Strategy, and create a
list used for authentication in index.js
:
const { Keystone } = require('@keystone-alpha/keystone');
const { AdminUI } = require('@keystone-alpha/admin-ui');
const { MongooseAdapter } = require('@keystone-alpha/adapter-mongoose');
const { Text, Password } = require('@keystone-alpha/fields');
const PasswordAuth = require('@keystone-alpha/keystone/auth/Password');
const keystone = new Keystone({
name: 'Keystone With Auth',
adapter: new MongooseAdapter(),
});
keystone.createList('User', {
fields: {
username: { type: Text },
password: { type: Password },
},
});
const authStrategy = keystone.createAuthStrategy({
type: PasswordAuth,
list: 'User',
{
identityField: 'username', // default: 'email'
secretField: 'password', // default: 'password'
}
});
const admin = new AdminUI(keystone, {
adminPath: '/admin',
authStrategy,
});
module.exports = {
keystone,
admin,
};
NOTE: It will be impossible to login the first time you load the Admin UI as there are no Users created. It is recommended to first run an instance of Keystone without an auth strategy, create your first User, then re-enable the auth strategy.
Developing
All source code should be formatted with Prettier.
Code is not automatically formatted in commit hooks to avoid unexpected behaviour,
so we recommended using an editor plugin to format your code as you work.
You can also run bolt format
to prettier all the things.
The lint
script will validate source code with both ESLint and prettier.
Setup
Keystone 5 is set up as a monorepo, using Bolt First, clone the Keystone 5 repository
git clone https://github.com/keystonejs/keystone-5.git
Then make sure you've got Bolt installed:
yarn global add bolt
Also make sure you have a local MongoDB server running (instructions). If you don't have it installed, on MacOS use Homebrew (run these once):
brew install mongodb
brew services start mongodb
Create an environment variable in the test project .env
. This will run project locally on port 3000
# CLOUDINARY_CLOUD_NAME=abc123
# CLOUDINARY_KEY=abc123
# CLOUDINARY_SECRET=abc123
PORT=3000
Then install the dependencies and start the test project:
bolt
bolt start {name of project folder}
(Running bolt start
will start the project located in demo-projects/todo
by default)
Testing
Keystone uses Jest for unit tests and Cypress for end-to-end tests. All tests can be run locally and on CircleCI.
Unit Tests
To run the unit tests, run the script:
bolt jest
Unit tests for each package can be found in packages/<package>/tests
and following the naming pattern <module>.test.js
.
To see test coverage of the files touched by the unit tests, run:
bolt jest --coverage
To see test coverage of the entire monorepo, including files which have zero test coverage, use the special script:
bolt coverage
End-to-End Tests
Keystone tests end-to-end functionality with the help of Cypress.
Each project (ie; test-projects/basic
, test-projects/login
, etc) have their own set of Cypress tests.
To run an individual project's tests, cd
into that directory and run:
bolt cypress:run
Cypress can be run in interactive mode from project directories with its built in GUI, which is useful when developing and debugging tests:
cd test-projects/basic && bolt cypress:open
End-to-end tests live in project/**/cypress/integration/*spec.js
.
It is possible to run all cypress tests at once from the monorepo root with the command:
bolt cypress:run
NOTE: The output from this command will mix together the output from each project being tested in parallel. This is only recommended as sanity check before pushing code.
Running a CI environment locally
Setting up CircleCI CLI tool
Install the circleci
cli tool:
If you've already got Docker For Mac installed (recommended)
brew install --ignore-dependencies circleci
If you do not have Docker installed
brew install circleci
Then make sure docker is able to share the following directories (in Docker for Mac, go Preferences
> File Sharing
):
- The keystone 5 repo
/Users/<your username>/.circleci
Run CI tests locally
Make sure Docker is running.
Execute the tests:
# Clean up the node_modules folders so everything is installed fresh
yarn clean
# Run the circle CI job
circleci local execute --job simple_tests
Where simple_tests
can be replaced with any job listed in
.circleci/config.yml
under the jobs:
section.
Arch - Keystone UI Kit
Resources, tooling, and design guidelines by KeystoneJS using GastbyJS
To start, run
bolt arch
License
Copyright (c) 2018 Jed Watson. Licensed under the MIT License.
Contributors
Thanks goes to these wonderful people (emoji key):
This project follows the all-contributors specification. Contributions of any kind welcome!