System Configuration API
The keystone-next
CLI expects to find a module named keystone.ts
with a default export of a Keystone system configuration returned from the function config()
.
import { config } from '@keystone-next/keystone';export default config({ /* ... */ });
The config
function accepts an object representing all the configurable parts of the system:
export default config({lists: { /* ... */ },db: { /* ... */ },ui: { /* ... */ },server: { /* ... */ },session: { /* ... */ },graphql: { /* ... */ },extendGraphqlSchema: { /* ... */ },images: { /* ... */ },experimental: { /* ... */ },});
We will cover each of these options below.
The configuration object has a TypeScript type of KeystoneConfig
, which can be imported from @keystone-next/keystone/types
.
This type definition should be considered the source of truth for the available configuration options.
lists
The lists
config option is where you define the data model, or schema, of the Keystone system.
It has a TypeScript type of ListSchemaConfig
.
This is where you define and configure the lists
and their fields
of the data model.
See the Schema API docs for details on how to use this function.
import type { ListSchemaConfig } from '@keystone-next/keystone/types';import { config } from '@keystone-next/keystone';export default config({lists: { /* ... */ },/* ... */});
db
import type { DatabaseConfig } from '@keystone-next/keystone/types';
The db
config option configures the database used to store data in your Keystone system.
It has a TypeScript type of DatabaseConfig
.
Keystone supports the database types PostgreSQL and SQLite.
These database types are powered by their corresponding Prisma database providers; postgresql
and sqlite
.
All database providers require the url
argument, which defines the connection URL for your database.
They also all have an optional onConnect
async function, which takes a KeystoneContext
object, and lets perform any actions you might need at startup, such as data seeding.
As well as these common options, each provider supports a number of optional advanced configuration options.
postgresql
Advanced configuration:
enableLogging
(default:false
): Enable logging from the Prisma client.useMigrations
(default:false
): Determines whether to use migrations or automatically force-update the database with the latest schema and potentially lose data.idField
(default:{ kind: "cuid" }
): The kind of id field to use, it can be one of:cuid
,uuid
orautoincrement
. This can also be customised at the list leveldb.idField
.prismaPreviewFeatures
(default:[]
): Enable Prisma preview features by providing an array of strings.
export default config({db: {provider: 'postgresql',url: 'postgres://dbuser:dbpass@localhost:5432/keystone',onConnect: async context => { /* ... */ },// Optional advanced configurationenableLogging: true,useMigrations: true,idField: { kind: 'uuid' },},/* ... */});
sqlite
Advanced configuration:
enableLogging
(default:false
): Enable logging from the Prisma client.useMigrations
(default:false
): Determines whether to use migrations or automatically force-update the database with the latest schema and potentially lose data.idField
(default:{ kind: "cuid" }
): The kind of id field to use, it can be one of:cuid
,uuid
orautoincrement
. This can also be customised at the list leveldb.idField
.
export default config({db: {provider: 'sqlite',url: 'file:./keystone.db',onConnect: async context => { /* ... */ },// Optional advanced configurationenableLogging: true,useMigrations: true,idField: { kind: 'uuid' },},/* ... */});
Limitations
The sqlite
provider is not intended to be used in production systems, and has certain limitations:
decimal
: Thedecimal
field type is not supported.timestamp
: Thetimestamp
field type only supports times within the range1970 - 2038
.text
: Thetext
field type does not support setting a filter as case sensitive or insensitive. Assuming default collation, all the filters exceptcontains
,startsWith
andendsWith
will be case sensitive andcontains
,startsWith
andendsWith
will be case insensitive but only for ASCII characters.select
: Using thetype: 'enum'
, the value will be represented as a string in the database.
ui
import type { AdminUIConfig } from '@keystone-next/keystone/types';
The ui
config option configures the Admin UI which is provided by Keystone.
It has a TypeScript type of AdminUIConfig
.
This config option is for top level configuration of the Admin UI.
Fine grained configuration of how lists and fields behave in the Admin UI is handled in the lists
definition (see the Schema API for more details).
Options:
isDisabled
(default:false
): IfisDisabled
is set totrue
then the Admin UI will be completely disabled.isAccessAllowed
(default:(context) => !!context.session
): This function controls whether a user is able to access the Admin UI. It takes aKeystoneContext
object as an argument.
Advanced configuration:
publicPages
(default:[]
): An array of page routes that can be accessed without passing theisAccessAllowed
check.getAdditionalFiles
(default:[]
): An async function returns an array ofAdminFileToWrite
objects indicating files to be added to the system atbuild
time. If themode
is'write'
, then the code to be written to the file should be provided as thesrc
argument. If themode
is'copy'
then aninputPath
value should be provided. TheoutputPath
indicates where the file should be written or copied to Note: This API is designed for use by plugins, such as the@keystone-next/auth
package. See the Custom Admin UI Pages guide for details on simpler ways to customise your Admin UI.
export default config({ui: {isDisabled: false,isAccessAllowed: async context => true,// Optional advanced configurationpublicPages: ['/welcome'],getAdditionalFiles: [async (config: KeystoneConfig) => [{mode: 'write',src: `/** @jsxRuntime classic *//** @jsx jsx */import { jsx } from '@keystone-ui/core';export default function Welcome() {return (<h1>Welcome to my Keystone system</h1>);}`,outputPath: 'pages/welcome.js',},{mode: 'copy',inputPath: '...',outputPath: 'pages/farewell.js',}],],},/* ... */});
server
import type { ServerConfig } from '@keystone-next/keystone/types';
The dev
and start
commands from the Keystone command line will start an Express web-server for you.
This server is configured via the server
configuration option.
Options:
cors
(default:undefined
): Allows you to configure the cors middleware for your Express server. If left undefinedcors
will not be used.port
(default:3000
): The port your Express server will listen on.maxFileSize
(default:200 * 1024 * 1024
): The maximum file size allowed for uploads. If left undefined, defaults to200 MiB
healthCheck
(default:undefined
): Allows you to configure a health check endpoint on your server.extendExpressApp
(default:undefined
): Allows you to extend the express app that Keystone creates.
export default config({server: {cors: { origin: ['http://localhost:7777'], credentials: true },port: 3000,maxFileSize: 200 * 1024 * 1024,healthCheck: true,extendExpressApp: (app, createContext) => { /* ... */ },},/* ... */});
healthCheck
If set to true
, a /_healthcheck
endpoint will be added to your server which will respond with { status: 'pass', timestamp: Date.now() }
.
You can configure the health check with a custom path and JSON data:
config({server: {healthCheck: {path: '/my-health-check',data: { status: 'healthy' },},},})
Or use a function for the data
config to return real-time information:
config({server: {healthCheck: {path: '/my-health-check',data: () => ({status: 'healthy',timestamp: Date.now(),uptime: process.uptime(),}),},},})
extendExpressApp
This lets you modify the express app that Keystone creates before the Apollo Server and Admin UI Middleware are added to it (but after the cors
and healthcheck
options are applied).
The function is passed two arguments:
app
: The express app keystone has createdasync createContext(req, res)
: A function you can call to create a Keystone Context for the request
For example, you could add your own request logging middleware:
export default config({server: {extendExpressApp: (app) => {app.use((req, res, next) => {console.log('A request!');next();});},},});
Or add a custom route handler:
export default config({server: {extendExpressApp: (app) => {app.get('/_version', (req, res) => {res.send('v6.0.0-rc.2');});},},});
You could also use it to add custom REST endpoints to your server, by creating a context for the request and using the Query API Keystone provides:
export default config({server: {extendExpressApp: (app, createContext) => {app.get('/api/users', async (req, res) => {const context = await createContext(req, res);const users = await context.query.User.findMany();res.json(users);});},},});
The created context will be bound to the request, including the current visitor's session, meaning access control will work the same as for GraphQL API requests.
session
import type { SessionStrategy } from '@keystone-next/keystone/types';
The session
config option allows you to configure session management of your Keystone system.
It has a TypeScript type of SessionStrategy<any>
.
In general you will use SessionStrategy
objects from the @keystone-next/keystone/session
package, rather than writing this yourself.
import { statelessSessions } from '@keystone-next/keystone/session';export default config({session: statelessSessions({ /* ... */ }),/* ... */});
See the Session API for more details on how to configure session management in Keystone.
graphql
import type { GraphQLConfig } from '@keystone-next/keystone/types';
The graphql
config option allows you to configures certain aspects of your GraphQL API.
It has a TypeScript type of GraphQLConfig
.
Options:
debug
(default:process.env.NODE_ENV !== 'production'
): Iftrue
, stacktraces from both Apollo errors and Keystone errors will be included in the errors returned from the GraphQL API. These can be filtered out withapolloConfig.formatError
if you need to process them, but do not want them returned over the GraphQL API.queryLimits
(default:undefined
): Allows you to limit the total number of results returned from a query to your GraphQL API. See also the per-listgraphql.queryLimits
option in the Schema API.path
(default:'/api/graphql'
): The path of the GraphQL API endpoint.cors
(default:rocess.env.NODE_ENV !== 'production' ? { origin: 'https://studio.apollographql.com', credentials: true } : undefined
): The CORS configuration to use on the GraphQL API endpoint. The default is setup to allow access access to API from the Apollo Sandbox during developmentapolloConfig
(default:undefined
): Allows you to pass extra options into theApolloServer
constructor.
export default config({graphql: {debug: process.env.NODE_ENV !== 'production',queryLimits: { maxTotalResults: 100 },path: '/api/graphql',apolloConfig: {debug: true,/* ... */},},/* ... */});
extendGraphqlSchema
import type { ExtendGraphqlSchema } from '@keystone-next/keystone/types';
The extendGraphqlSchema
config option allows you to extend the GraphQL API which is generated by Keystone based on your schema definition.
It has a TypeScript type of ExtendGraphqlSchema
.
In general you will use the function graphQLSchemaExtension({ typeDefs, resolvers })
to create your schema extension.
import { config, graphQLSchemaExtension } from '@keystone-next/keystone';export default config({extendGraphqlSchema: graphQLSchemaExtension({ typeDefs, resolvers }),/* ... */});
See the schema extension guide for more details on how to use graphQLSchemaExtension()
to extend your GraphQL API.
files
Keystone supports file handling via the file
field type.
In order to use this field type you need to configure Keystone with information about where your files will be stored and served from.
At the moment Keystone supports storing files on the local filesystem, and is agnostic about how files are served.
import { config } from '@keystone-next/keystone';export default config({files: {upload: 'local',local: {storagePath: 'public/files',baseUrl: '/files',},}/* ... */});
Options:
upload
: The storage target when uploading files. Currently onlylocal
is supported.local
: Configuration options when using thelocal
storage target.storagePath
: The path local files are uploaded to.baseUrl
: The base of the URL local files will be served from, outside of keystone.
images
Keystone supports image handling via the image
field type.
In order to use this field type you need to configure Keystone with information about where your images will be stored and served from.
At the moment Keystone supports storing files on the local filesystem, and is agnostic about how images are served.
import { config } from '@keystone-next/keystone';export default config({images: {upload: 'local',local: {storagePath: 'public/images',baseUrl: '/images',},}/* ... */});
Options:
upload
: The storage target when uploading images. Currently onlylocal
is supported.local
: Configuration options when using thelocal
storage target.storagePath
: The path local images are uploaded to.baseUrl
: The base of the URL local images will be served from, outside of keystone.
experimental
The following flags allow you to enable features which are still in preview. These features are not guaranteed to work, and should be used with caution.
import { config } from '@keystone-next/keystone';export default config({experimental: {enableNextJsGraphqlApiEndpoint: true,generateNextGraphqlAPI: true,generateNodeAPI: true,}/* ... */});
Options:
enableNextJsGraphqlApiEndpoint
: (coming soon)generateNextGraphqlAPI
: Creates a file atnode_modules/.keystone/next/graphql-api
withdefault
andconfig
exports that can be re-exported in a Next API routegenerateNodeAPI
: Creates a file atnode_modules/.keystone/api
with alists
export