System Configuration
The keystone 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-6/core'export default config({/* ... */})
The config function accepts an object representing all the configurable parts of the system:
export default config({lists: {/* ... */},listDefaults: {/* ... */},db: {/* ... */},ui: {/* ... */},server: {/* ... */},session: {/* ... */},graphql: {/* ... */},extendGraphqlSchema: {/* ... */},})
We will cover each of these options below.
The configuration object has a TypeScript type of KeystoneConfig, which can be imported from @keystone-6/core/types. This type definition should be considered the source of truth for the available configuration options.
Note: It is important to pass a TypeInfo type argument to the config function as it ensures proper typing for the Keystone Context. This type is automatically created in generated/keystone/types. You can customize the output path of the generated type by specifying it in the config object.
import { TypeInfo } from './generated/keystone/types'export default config<TypeInfo>({/* ... */})
lists
The lists config option is where you define the data model, or schema, of the Keystone system. This is where you define and configure the lists and their fields of the data model. See the Lists API docs for details on how to use this function.
import { config } from '@keystone-6/core'import { TypeInfo } from './generated/keystone/types'export default config<TypeInfo>({lists: {/* ... */},/* ... */})
listDefaults
The listDefaults config option provides defaults for every list. It supports graphql.omit and graphql.maxTake:
export default config<TypeInfo>({listDefaults: {graphql: {omit: { delete: true },maxTake: 100,},},lists: {Post: list({fields: { /* ... */ },graphql: {omit: false,},}),AuditLog: list({fields: { /* ... */ },graphql: {maxTake: Infinity,},}),},/* ... */});
Per-list configuration takes precedence. Boolean omit values replace the default entirely, while object values inherit operations that are not specified. An undefined maxTake inherits the default; set it to Infinity to make a list unlimited.
db
The db config option configures the database used to store data in your Keystone system. Keystone supports the database types PostgreSQL, MySQL and SQLite. These database types are powered by their corresponding Prisma database providers; postgresql, mysql and sqlite.
provider: The database provider to use, it can be one ofpostgresql,mysqlorsqlite.prismaClientOptions: A function returning the options for the Prisma client. This must include anadapterfor your database.prismaClientPath(default:generated/prisma): The output directory for the generated Prisma client.prismaSchemaPath(default:schema.prisma): The path where Keystone writes its generated Prisma schema.extendPrismaClient: A function that receives the generated Prisma Client instance and returns a client extended with custom functionality.extendPrismaSchema: A function that receives the generated Prisma schema and returns a customised schema.onConnect: which takes aKeystoneContextobject, and lets perform any actions you might need at startup, such as data seedingidField(default:{ kind: "cuid" }): The kind of id field to use, it can be one of:cuid,uuid,nanoid,ulid, orautoincrement. This can also be customised at the list leveldb.idField. If you are usingautoincrement, you can also specifytype: 'BigInt'on PostgreSQL and MySQL to use BigInts.
The Database URL used by the Prisma CLI and prisma db push used in keystone dev are defined in the prisma.config.ts. Keystone creates an initial config if it is missing, but never overwrites the config.
postgresql
import { PrismaPg } from '@prisma/adapter-pg'export default config<TypeInfo>({db: {provider: 'postgresql',prismaClientOptions: () => ({adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL! }),log: ['warn', 'error'],}),onConnect: async context => {/* ... */},idField: { kind: 'uuid' },},/* ... */})
mysql
import { PrismaMariaDb } from '@prisma/adapter-mariadb'export default config<TypeInfo>({db: {provider: 'mysql',prismaClientOptions: () => ({adapter: new PrismaMariaDb(process.env.DATABASE_URL!),}),onConnect: async context => {/* ... */},idField: { kind: 'uuid' },},/* ... */})
sqlite
import { PrismaBetterSqlite3 } from '@prisma/adapter-better-sqlite3'export default config<TypeInfo>({db: {provider: 'sqlite',prismaClientOptions: () => ({adapter: new PrismaBetterSqlite3({ url: 'file:./keystone.db' }),}),onConnect: async context => {/* ... */},idField: { kind: 'uuid' },},/* ... */})
Limitations
The sqlite provider is not intended to be used in production systems, and has certain limitations:
decimal: Thedecimalfield type is not supported.timestamp: Thetimestampfield type only supports times within the range1970 - 2038.text: Thetextfield type does not support setting a filter as case sensitive or insensitive. Assuming default collation, all the filters exceptcontains,startsWithandendsWithwill be case sensitive andcontains,startsWithandendsWithwill be case insensitive but only for ASCII characters.select: Using thetype: 'enum', the value will be represented as a string in the database.
ui
The ui config option configures the Admin UI which is provided by Keystone. 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 Lists API for more details).
Options:
isDisabled(default:false): IfisDisabledis set totruethen the Admin UI will be completely disabled.isAccessAllowed(default:(context) => context.session !== undefined): This function controls whether a user can view the Admin UI. It takes aKeystoneContextobject as an argument.
Advanced configuration:
publicPages(default:[]): An array of page routes that bypass theisAccessAllowedfunction.pageMiddleware(default:undefined): An async middleware function that can optionally return a redirectgetAdditionalFiles(default:undefined): An async function that returns an array ofAdminFileToWriteobjects indicating files to be added to the system atbuildtime. If themodeis'write', then the code to be written to the file should be provided as thesrcargument. If themodeis'copy'then aninputPathvalue should be provided. TheoutputPathindicates where the file should be written or copied to Note: This API is designed for use by plugins, such as the@keystone-6/authpackage. See the Custom Admin UI Pages guide for details on simpler ways to customise your Admin UI.
export default config<TypeInfo>({ui: {isDisabled: false,isAccessAllowed: async context => context.session !== undefined,// advanced configurationpublicPages: ['/welcome'],getAdditionalFiles: async () => [{mode: 'write',src: `import { Heading } from '@keystar/ui/typography';export default function Welcome() {return (<h1>Welcome to my Keystone system</h1>);}`,outputPath: 'pages/welcome.js',},{mode: 'copy',inputPath: '...',outputPath: 'pages/farewell.js',},],},/* ... */})
server
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 undefinedcorswill not be used.port(default:3000): The port your Express server will listen on.options(default:undefined): Thehttp.createServeroptions used by Node.maxFileSize(default:200 * 1024 * 1024): The maximum file size allowed for uploads. If left undefined, defaults to200 MiBextendExpressApp(default:undefined): Allows you to extend the express app that Keystone creates.extendHttpServer(default:undefined): Allows you to extend the nodehttpserver that runs Keystone.
export default config<TypeInfo>({server: {cors: { origin: ['http://localhost:7777'], credentials: true },port: 3000,maxFileSize: 200 * 1024 * 1024,extendExpressApp: async (app, commonContext) => {/* ... */},extendHttpServer: async (httpServer, commonContext) => {/* ... */},},/* ... */})
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 createdcontext: A Keystone Context
For example, you could add your own request logging middleware:
export default config<TypeInfo>({server: {extendExpressApp: app => {app.use((req, res, next) => {console.log('A request!')next()})},},})
Or add a custom route handler:
export default config<TypeInfo>({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<TypeInfo>({server: {extendExpressApp: (app, commonContext) => {app.get('/api/users', async (req, res) => {const context = await commonContext.withRequest(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.
ProTip!: extendExpressApp can be async
extendHttpServer
This lets you interact with the node http.Server that Keystone uses.
The function is passed in 3 arguments:
server- this is the HTTP server that you can then extendcontext: A Keystone Context
For example, this function could be used to listen for 'upgrade' requests for a WebSocket server when adding support for GraphQL subscriptions
import { WebSocketServer } from 'ws'import { useServer as wsUseServer } from 'graphql-ws/lib/use/ws'export default config<TypeInfo>({server: {extendHttpServer: (httpServer, commonContext) => {const wss = new WebSocketServer({server: httpServer,path: '/api/graphql',})wsUseServer({ schema: commonContext.graphql.schema }, wss)},},})
Note: when using keystone dev, extendHttpServer is only called once on startup - you will need to restart your process for any updates
session
The session config option allows you to configure session management of your Keystone system.
In general you will use SessionStrategy objects from the @keystone-6/core/session package, rather than writing this yourself.
import { statelessSessions } from '@keystone-6/core/session'export default config<TypeInfo>({session: statelessSessions({/* ... */}),/* ... */})
See the Session API for more details on how to configure session management in Keystone.
graphql
The graphql config option allows you to configure certain aspects of your GraphQL API.
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.formatErrorif you need to process them, but do not want them returned over the GraphQL API.path(default:'/api/graphql'): The path of the GraphQL API endpoint.playground(default:process.env.NODE_ENV !== 'production')true- AddApolloServerPluginLandingPageGraphQLPlaygroundto the Apollo Server pluginsfalse- AddApolloServerPluginLandingPageDisabledto the Apollo Server plugins'apollo'- Do not add any plugins to the Apollo config, this will use Apollo Sandbox
apolloConfig(default:undefined): Allows you to pass extra options into theApolloServerconstructor.schemaPath(default:schema.graphql): The path of the generated GraphQL API schema.
export default config<TypeInfo>({graphql: {debug: process.env.NODE_ENV !== 'production',path: '/api/graphql',apolloConfig: {debug: true,/* ... */},},/* ... */})
extendGraphqlSchema
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 (schema: import("graphql").GraphQLSchema) => import("graphql").GraphQLSchema.
extendGraphqlSchema expects a function that takes the GraphQL Schema generated by Keystone and returns a valid GraphQL Schema
import type { GraphQLSchema } from 'graphql'import { config, g } from '@keystone-6/core'export default config<TypeInfo>({extendGraphqlSchema: (keystoneSchema: GraphQLSchema) => {/* ... */return newExtendedSchema},/* ... */})
See the schema extension guide for more details and tooling options on how to extend your GraphQL API.
OpenTelemetry
Keystone has built-in support for OpenTelemetry tracing via @opentelemetry/api. When you configure an OpenTelemetry SDK in your application, Keystone will automatically emit trace spans for its internal operations (such as CRUD resolvers and hooks).
No additional Keystone configuration is required — just set up the OpenTelemetry SDK before Keystone starts. See the logging-opentelemetry example for a complete setup.