Skip to Page NavigationSkip to Page NavigationSkip to Content
Keystone 6 is in Community Preview! What does that mean? see our Roadmap. For Keystone 5 docs, visit v5.keystonejs.com

Release: 2nd June 2021

What's New

We have a new JSON field ✨, a bunch of new learning resources, and plenty of under the hood optimisations in this big release. πŸ’ͺ

"@keystone-next/adapter-prisma-legacy": "8.0.0",
"@keystone-next/admin-ui-utils": "5.0.1",
"@keystone-next/auth": "26.0.0",
"@keystone-next/cloudinary": "5.0.1",
"@keystone-next/fields": "10.0.0",
"@keystone-next/fields-document": "6.0.1",
"@keystone-next/keystone": "19.0.0",
"@keystone-next/test-utils-legacy": "20.0.0",
"@keystone-next/types": "19.0.0",
"@keystone-next/utils-legacy": "11.0.1",

JSON Field πŸ‘©πŸ»β€πŸ’»

Thanks to the new json field, you can now represent JSON blobs in your backend. Check out the JSON example project to learn more.

Package: list({
fields: {
pkgjson: json({ isRequired: true }),
isPrivate: checkbox(),
ownedBy: relationship({ ref: 'Person.packages', many: false }),
},
}),

More Learning Resources πŸ§‘β€πŸ«

In addition to the JSON one above, we added new examples for:

We also published a tutorial that shows you how to embed Keystone and SQLite in a Next.js app. The end result is an app with a queryable GraphQL endpoint based on your Keystone schema that you can run live on Vercel – for free! πŸš€

sortBy deprecated with improvements to orderBy

We deprecated the sortBy GraphQL filter and updated the orderBy GraphQL filter with an improved API.

Previously a User list's allUsers query would have the argument:

orderBy: String

The new API gives it the argument:

orderBy: [UserOrderByInput!]! = []

where

input UserOrderByInput {
id: OrderDirection
name: OrderDirection
score: OrderDirection
}
enum OrderDirection {
asc
desc
}

Rather than writing allUsers(orderBy: "name_ASC") you now write allUsers(orderBy: { name: asc }). You can also order by multiple fields, e.g. allUsers(orderBy: [{ score: asc }, { name: asc }]).

Note: each UserOrderByInput must have exactly one key, or else an error will be returned.

withItemData replaced with sessionData πŸ”§

We removed withItemData in favour of a sessionData option to the createAuth() function.

Previously, withItemData would be used to wrap the config.session argument:

import { config, createSchema, list } from '@keystone-next/keystone/schema';
import { statelessSessions, withAuthData } from '@keystone-next/keystone/session';
import { text, password, checkbox } from '@keystone-next/fields';
import { createAuth } from '@keystone-next/auth';
const { withAuth } = createAuth({
listKey: 'User',
identityField: 'email',
secretField: 'password',
});
const session = statelessSessions({ secret: '-- EXAMPLE COOKIE SECRET; CHANGE ME --' });
export default withAuth(
config({
lists: createSchema({
fields: {
email: text({ isUnique: true }),
password: password(),
isAdmin: checkbox(),
},
}),
session: withItemData(session, { User: 'id isAdmin' }),
}),
})
);

Now, the fields to populate are configured on sessionData in createAuth, and withItemData is completely removed.

import { config, createSchema, list } from '@keystone-next/keystone/schema';
import { statelessSessions } from '@keystone-next/keystone/session';
import { text, password, checkbox } from '@keystone-next/fields';
import { createAuth } from '@keystone-next/auth';
const { withAuth } = createAuth({
listKey: 'User',
identityField: 'email',
secretField: 'password',
sessionData: 'id isAdmin',
});
const session = statelessSessions({ secret: '-- EXAMPLE COOKIE SECRET; CHANGE ME --' });
export default withAuth(
config({
lists: createSchema({
fields: {
email: text({ isUnique: true }),
password: password(),
isAdmin: checkbox(),
},
}),
session,
}),
})
);

More consistent and predictable createItems, updateItems, and deleteItems mutations πŸ§˜β€β™€οΈ

We fixed the behaviour of createItems, updateItems, and deleteItems mutations to be consistent and predictable.

Previously, these mutations could return items in an arbitrary order. They now return items in the same order they were provided to the mutation.

Previously, if there was an error (e.g. validation) on one or more of the items – the return value would be null and a single top level error would be returned. The state of the database in this case was non-deterministic.

The new behaviour is to return values for all items created, with null values for those that had errors. These errors are returned in the errors array and have paths which correctly point to the null values in the returned array. All the valid operations will be completed, leaving the database in a deterministic state.

Previously, if items were filtered out by declarative access control, then no error would be returned, and only those accessible items would be returned. Now the returned data will contain null values for those items which couldn't accessed, and the errors array will contain errors with paths which correctly point to the null values in the returned array.

Previously, if static access control denied access to the mutation, then null was returned, and a single error was returned. Now, an array of nulls is returned, with a separate error for each object. This makes the behaviour of static and declarative access control consistent.

Counts Improved πŸ”’

The GraphQL field _all<path>Meta { count } generated for many relationships has been deprecated in favour of a new field <path>Count, which directly returns the count.

A posts relationship field would have the following field added to the API:

postsCount(where: PostWhereInput! = {}): Int

Prisma updated to 2.24.0 ⬆️

We've updated our Prisma dependency from 2.22.1 to 2.24.0! Check out the Prisma release notes for more details.

Credits Party popper

  • Thanks @jonowu for adding a sameSite option to the session options for cookies. Can be one of true, false, 'strict', 'lax' or 'none' as per Mozilla docs. See the PR for more details!

  • Thanks @gabrielkuettel for fixing a typo Database Items API page!

You can also view the verbose release notes on GitHub.