Concepts

KeystoneJS requires MongoDB v2.4 or greater.

In KeystoneJS, your data schema and models are controlled by Lists, and documents in your database are often called Items.

To define a data model, you create a new keystone.List, and pass it list options.

You then add fields to the list. Behind the scenes, a Keystone List will create a mongoose schema, and add the appropriate paths to it for the fields you define.

The schema is accessible, allowing you to plug in other mongoose functionality like virtuals, methods and pre / post hooks.

When you have finished setting up your List, call list.register() to initialise it and register it with Keystone.

To query your data, you use the list.model (which is a mongoose model).

List Items are mongoose documents. To create new items, use new list.model() and when you're ready to save it (or to save changes to an existing Item), call item.save().

Lists

Usage

new keystone.List(key[, options]);

The syntax for creating a Keystone List is very similar to the syntax for creating a Mongoose Schema, with the exception of the constructor, which is var MyList = new keystone.List(key, options).

Once you have created a new List, add fields to it using MyList.add(fields), where fields is an object of keys (for field paths) and values (for field types, or options).

Fields are defined by an object with a type property, which must be a valid Field Type or basic data type. Using the object syntax you can specify additional options for the field. Common field options and field-type-specific options are detailed in the fields documentation.

When all the fields and options have been set on the list, call MyList.register() to register the list with Keystone and finalise its configuration.

Example

A simple Post model for a blog might look like this:

Post.js

var keystone = require('keystone'),
    Types = keystone.Field.Types;
 
var Post = new keystone.List('Post', {
    autokey: { path: 'slug', from: 'title', unique: true },
    map: { name: 'title' },
    defaultSort: '-createdAt'
});
 
Post.add({
    title: { type: String, required: true },
    state: { type: Types.Select, options: 'draft, published, archived', default: 'draft' },
    author: { type: Types.Relationship, ref: 'User' },
    createdAt: { type: Date, default: Date.now },
    publishedAt: Date,
    image: { type: Types.CloudinaryImage },
    content: {
        brief: { type: Types.Html, wysiwyg: true, height: 150 },
        extended: { type: Types.Html, wysiwyg: true, height: 400 }
    }
});
 
Post.defaultColumns = 'title, state|20%, author, publishedAt|15%'
Post.register();

This example implements the optional map, autokey and defaultSort options, described below.

It also specifies title, state, author and publishedAt as the default columns to display in the Admin UI, with state and publishedAt being given column widths.

The author field is a relationship with the User model, as described in the getting started guide.

Options

Lists support the following options:

label String The label used for the list in the Admin UI. Defaults to a friendly form of key
path String The path used for the list in the Admin UI. Defaults to a slugified form of key.
singular String The singular label for the items in the list. Used in the Admin UI, defaults to a singular form of label
plural String The plural label for the items in the list. Used in the Admin UI, defaults to a plural form of singular
schema String

Options for the Mongoose Schema for the List. Among other things, this option lets you specify a custom name for the collection. See the mongoose schema docs for a list of available options.

Warning: do not modify the id or _id schema options; the default behaviour is required by Keystone

drilldown String A space-delimited list of relationships to display as drilldown in the Admin UI
inherits List A list object that you would like to inherit fields from.
sortable Boolean Adds a hidden field sortOrder to the schema, and enables drag and drop sorting in the Admin UI
sortContext String A List:relationship pair to control when drag and drop sorting is available in the Admin UI
searchFields String A space-delimited list of paths to use for searching in Admin UI
defaultSort String The default column or path to sort on in the Admin UI
defaultColumns String A comma-delimited list of default columns to display in the Admin UI List View. You can specify width in either pixels or percent after a | pipe character.
map Object An object that maps fields to special list paths. Each path defaults to its key if a field with that key is added. Mappable paths include
  • name - the field that contains the name of the item, for display in the Admin UI
autokey Object Adds a plugin to the list that automatically generates a key for each document when it is saved, based on the value of another field or path. The value of the option should be an object with the following keys:
  • from String - the field or path to generate the key from, can be a space-delimited list of fields
  • path String - the path to store the key at
  • unique Boolean - whether the key should be unique or not
  • fixed Boolean - the key should be kept if it exists and it's non-empty. Defaults to false.
Autokey paths are automatically indexed; you may also want to include them in compound indexes.
track Boolean or Object

Adds a plugin to the list that automatically keeps track of when and who (i.e. which Keystone user) created and last updated an item.

When set to true all tracking fields are enabled using their default names.

You may also selectively enable each field and optionally specify a custom field name by setting track to an object with any or all the following fields:

  • createdAt Boolean/String - when set to true, enables tracking when the item was created (using the default field name createdAt). To use a custom field name set to a String with the desired name. Defaults to false.
  • createdBy Boolean/String - when set to true, enables tracking which user created the item (using the default field name createdBy). To use a custom field name set to a String with the desired name. Defaults to false.
  • updatedAt Boolean/String - when set to true, enables tracking when the item was last updated (using the default field name updatedAt). To use a custom field name set to a String with the desired name. Defaults to false.
  • updatedBy Boolean/String - when set to true, enables tracking which user last updated the item (using the default field name updatedBy). To use a custom field name set to a String with the desired name. Defaults to false.

The createdBy and updatedBy fields are only automatically updated when adding/modifying items via the Keystone Admin UI. However, if you intend to add/modify items from within your own app, you must manually set the ._req_user property of the item to the currently logged on user (req.user) prior to saving the item, as shown in the example below.

var item = new List.model();
item.set({ field1: 'value1', field2: 'value2' });
item._req_user = req.user;
item.save();
noedit Boolean Prevents editing of items in the list through the Keystone Admin UI
nocreate Boolean Prevents creation of new items in the list through the Keystone Admin UI
nodelete Boolean Prevents deletion of items from the list through the Keystone Admin UI
hidden Boolean Hides the list in the Keystone Admin UI

If you're wondering how to control which navigation area Lists are categorised under in the Admin UI, check out the nav option in the KeystoneJS Configuration docs.

Drilldown example

The drilldown option is a nice way to improve the usability of the Admin UI by providing context to the item a user is currently editing.

By default, the drilldown will just show the list that the item belongs to.

You can, however, set it to a Relationship field in the schema, and it will display the item currently stored in that relationship field.

If there would be several relationships that may be relevant to display in the drilldown list, you can separate their paths with spaces.

Example: Including the author in the drilldown for Posts

var Post = new keystone.List('Post', {
    autokey: { path: 'slug', from: 'title', unique: true },
    map: { name: 'title' },
    defaultSort: '-createdAt',
    drilldown: 'author' // author is defined as a Relationship field in the example above
});

Inheritance example

The inheritance option can be used to allow a list to inherit its fields from another list using Mongoose model discriminators

Parent lists may not themselves inherit from other lists.

Example: Inheriting List fields from other lists

var keystone = require('keystone');
 
var BasePage = new keystone.List('BasePage', {
   map: { name: 'title' },
	    autokey: { path: 'slug', from: 'title', unique: true },
	});
BasePage.add(
	{
		title: { type: String, required: true },
		slug: { type: String, readonly: true },
	}
);
BasePage.register();
 
var ChildPage = new keystone.List('ChildPage', { inherits: BasePage });
ChildPage.add({ child_content: { type: String, readonly: true } });
ChildPage.register();

Schema Plugins

You can specify virtuals, methods, statics as well as pre and post hooks for your Lists using the schema. You can also use mongoose plugins from the plugins website.

For example, in our Post list above, we might want to automatically set the publishedAt value when the state is changed to published (but only if it hasn't already been set).

We might also want to add a method to check whether the post is published, rather than checking the state field value directly.

Before calling Post.register(), we would add the following code:

Post.schema.methods.isPublished = function() {
    return this.state == 'published';
}
 
Post.schema.pre('save', function(next) {
    if (this.isModified('state') && this.isPublished() && !this.publishedAt) {
        this.publishedAt = new Date();
    }
    next();
});

Querying Data

To query data, you can use any of the mongoose query methods on the list.model.

For example: to load the last 5 posts with the state published, populating the linked author, sorted by reverse published date:

Loading Posts

var keystone = require('keystone'),
    Post = keystone.list('Post');
 
Post.model.find()
    .where('state', 'published')
    .populate('author')
    .sort('-publishedAt')
    .limit(5)
    .exec(function(err, posts) {
        // do something with posts
    });

Promises

There exists another way to work with events in Javascript that is included in mongoose query methods. Instead of passing a callback to the exec method, we can use what it returns: a Promise. Promises are very useful for clean chaining of events with propagation of error.

For example: load 100 posts, then do something asynchronous, then do something with result:

Loading Posts, doing something asynchronous, doing something

var keystone = require('keystone'),
    Post = keystone.list('Post');
 
Post.model.find()
    .limit(100)
    .exec()
    .then(function (posts) { //first promise fulfilled
        //return another async promise
    }, function (err) { //first promise rejected
        throw err;
    }).then(function (result) { //second promise fulfilled
        //do something with final results
    }, function (err) { //something happened
        //catch the error, it can be thrown by any promise in the chain
        console.log(err);
    });

Pagination Querying

To query data with pagination, you can use List.paginate(),it returns a query object, just as List.model.find() would. It supports the options

  • page - page to start at
  • perPage - number of results to return per page
  • maxPages - optional, causes the page calculation to omit pages from the beginning/middle/end(useful if you have lots of pages, and do not want them to wrap over several lines).

For example: to load the posts with the maxPages 10 and perPage 10,which state is published, populating the linked author and categories, sorted by reverse published date:

Loading Posts with paginate

var keystone = require('keystone'),
    Post = keystone.list('Post');
 
 Post.paginate({
		page: req.query.page || 1,
		perPage: 10,
		maxPages: 10
	})
	.where('state', 'published')
	.sort('-publishedDate')
	.populate('author categories')
	.exec(function(err, results) {
		locals.data.posts = results;
		next(err);
	});

When you call exec on a paginated query, it will return a lot of metadata along with the results:

  • total: all matching results (not just on this page)
  • results: array of results for this page
  • currentPage: the index of the current page
  • totalPages: the total number of pages
  • pages: array of pages to display
  • previous: index of the previous page, false if at the first page
  • next: index of the next page, false if at the last page
  • first: the index of the first result included
  • last: index of the last result included

Creating Items

To create new items, again use the mongoose model:

Creating Posts

var keystone = require('keystone'),
    Post = keystone.list('Post');
 
var newPost = new Post.model({
    title: 'New Post'
});
 
if (shouldBePublished) {
    newPost.state = 'published';
}
 
newPost.save(function(err) {
    // post has been saved	
});

Automatic keys

Because we set the autokey option on our Post list, it will have generated a unique key based on the title before it was saved to the database.

newPost.slug == 'new-post';

Deleting Items

To delete items, first load the data, then use the remove method:

Deleting a Post

var keystone = require('keystone'),
    Post = keystone.list('Post');
 
Post.model.findById(postId)
    .remove(function(err) {
        // post has been deleted
    });

Headings

Define headings to display within the flow of your documents. Headings can be defined as a String or Object and can depend on another field value for display.

Person.add(
	'User', 
	{ name: { type: Types.Name, required: true, index: true, initial: true } }, 
	'Permissions', 
	{ isAdmin: { type: Boolean, label: 'Can access Keystone', index: true } },
	// header object
	{ heading: 'Activities' }, 
	{ place: { type: Types.Select, options: ['GT', 'UGA'] } },
	// header with dependsOn
	{ heading: "GT Activities", dependsOn: { place: 'GT' } },
	{ type: { type: Types.Select, options: ['ZC', 'MP'], dependsOn: { place: 'GT'} }
);
Options

heading String - the text to display

dependsOn Object - heading will only be displayed when the paths specified in the object match the current data for the item. dependsOn

Fields

When adding fields to Lists, you can either specify basic data types or Keystone Field Types.

Overview

Keystone Fields allow you to easily add rich, functional fields to your application's models. They are designed to describe not just the structure of your data, but also the intention of your data. They provide:

  • Rich controls in Keystone's Admin UI
  • Complex data types; e.g. the location field stores several strings and an GeoJSON lng/lat point
  • Formatting and validation methods
  • Additional virtual properties; e.g. the name field provides a name.full virtual which concatenates the stored name.first and name.last
  • Underscore methods; e.g. the password field provides a password.compare method for testing against the encrypted hash
  • Metadata about how fields relate to each other; e.g. which fields depend on certain values in other fields

Basic data types are mapped to their corresponding Keystone field types:

Data type Field type
String Text
Number Number
Date DateTime
Boolean Boolean

Field Options

All field types support several common options, which can specify database settings (such as index and default), or can provide information for Keystone's Admin UI (such as label).

Fields can be nested inside objects, as in mongoose schemas.

All mongoose schema type options are passed to the mongoose schema, so you can also use any options mongoose supports.

Common field options include:

label String The label of each field is generated from the field path; set this option to override the default.
required Boolean Validates that the field has a value before an item can be saved (also passed to mongoose and enforced using a database index).
initial Boolean Causes the field to be displayed in the Create Item form, in the Admin UI.
noedit Boolean Renders the field as read-only in the admin UI.
note String Is displayed with the field in the admin UI.
hidden Boolean The field will always be hidden in the Admin UI if this is set to true

Conditional Fields

To improve the usability of the Admin UI, it is possible to hide fields when no value is set, or depending on the value of other fields.

collapse Boolean Displays an + add link in the admin UI when the field has no value. Will completely hide field UI when noedit is also set to true, when the field has no value
dependsOn Object

The field or header will only be displayed when the paths specified in the object match the current data for the item.

You can target multiple values per path using an Array.

Example

first: { type: String },
// Will show if first === "value1", "1" or "2"
second: { type: String, dependsOn: { first: ['value1', '1', 2] } },
// Will show if first == "value1"
third: { type: String, dependsOn: { first: 'value1' } }

Generated values and watching fields

Keystone's fields support a simple syntax for configuring dynamically updated fields. You can set a field to update its value whenever:

  • The item is saved
  • The value of any other field (or fields) changes
  • The value of any other field (or fields) changes to a specific value

To use the watching functionaliy, set the following two options:

watch Boolean or String or Object or Function

When true, the field value will be recalculated every time an item is saved.

Provide a space-delimited list of paths to recalculate the field value whenever one of those paths changes.
For example: 'author title state'

Provide an object of key / value pairs to recalculate the field value whenever one of those paths changes to the value specified.
For example: {'state': 'published', 'mainPost': true}

Provide a function that returns true/false whenever you want.
For example: function() { return this.author === this.editor; }

value Function

The function to generate the field value when a watched path is changed. Must return the new value, or accept a node-style callback argument, which can be called to set the field value asynchronously.

The this context of the function will be the item being saved.

Example (synchronous)

function () {
    return this.total<=this.totalreceived ? true:false;
}

Example (asynchronous)

function (callback) { // BEWARE: MUST be called "callback" to allow asynchronous execution
	list.model.findById(this.createdBy).exec(function(err, user){
		callback(err, user.name + "-" + Date.now());
	});
}

Underscore Methods

Some field types include helpful underscore methods, which are available on the item at the field's path preceded by an underscore.

For example: use the format underscore method of the createdAt DateTime field of the Posts List (above) like this

var keystone = require('keystone'),
    Post = keystone.list('Post');
 
Post.model.findById(postId).exec(function(err, post) {
   console.log(post._.createdAt.format('Do MMMM YYYY')); // 25th August 2013
});

Relationships

Keystone enhances MongoDB's ability to store the ObjectIDs of related documents in a field (or many related ObjectIDs in an Array) with support for Relationship fields and Definitions in Models.

Relationship Fields

ObjectId or Array — Displayed as an auto-suggest field in the Admin UI

Stores references to ObjectIDs from another Model in an ObjectID field or array to create one-many or many-many relationships.

Specify the related Model using the ref option. For a many-many relationship, set the many option to true.

For example, if you wanted to link a Post model to a single Author and many PostCategories, you would do it like this:

Post.add({
    author: { type: Types.Relationship, ref: 'User' },
    categories: { type: Types.Relationship, ref: 'PostCategory', many: true }
});
Relationship Filters

You can filter a relationship field using the filters option.

The filters option is an object of key/value pairs, in which the keys correspond to the fields of the related model to be filtered, and the values will either be literals or field names in the current model, the value of which will be used to filter the relationship.

In the example below, the author field will only allow selection of a User whose group field is equal to 'admin'.

Post.add({
    title: { type: String, required: true },
    category: { type: Types.Select, options: 'user, editor, admin', default: 'user' },
    author: { type: Types.Relationship, ref: 'User', filters: { group: 'admin' } }
});

You can also filter by the value of another field on the model. You do this setting the value of the filter to the name of the field, prefixed by a colon (:).

In the example below, the author field will only allow selection of a User whose group field is equal to the value of the category field of the Post model.

Post.add({
    title: { type: String, required: true },
    category: { type: Types.Select, options: 'user, editor, admin', default: 'user' },
    author: { type: Types.Relationship, ref: 'User', filters: { group: ':category' } }
});

Finally, you can also filter by the current model's _id field.

In the example below, the bestPost field will only allow selection of a Post whose author field is equal to the _id of the current document.

User.add({
    name: { type: String, required: true },
    group: { type: Types.Select, options: 'user, editor, admin', default: 'user' },
    bestPost: { type: Types.Relationship, ref: 'Post', filters: { author: ':_id' } }
});

You can only set filters on one-many relationships (i.e. when the many option is NOT set to true).

Populating related data in queries

You can populate related data for relationship fields thanks to Mongoose's populate functionality. To populate the author and category documents when loading a Post from the example above, you would do this:

Post.model.findOne().populate('author categories').exec(function(err, post) {
    // the author is a fully populated User document
    console.log(post.author.name);
});

Note that if no ObjectId is stored, or an invalid ObjectId is stored (e.g. a document has been deleted), author will be undefined in the example above.

Relationship Definitions

What if, in the example above, you wanted to see a list of the Posts by each Author? Because the relationship field is on the Post, you need to tell the Author (and the PostCategory) Model that it is being referred to. Doing so allows the Admin UI to represent the relationship from both sides.

You do this by calling the relationship method on the Model like this:

User.relationship({ path: 'posts', ref: 'Post', refPath: 'author' });
Options

path String - the path of the relationship reference on the Model

ref String - the key of the referred Model (the one that has the relationship field)

refPath String - the path of the relationship being referred to in the referred Model

As you can see, the options provided to the relationship method mirror those of the relationship field it refers to.

Relationship definitions are optional; if you leave them out, the relationships simply won't be displayed in the Admin UI from the other side of the relationship. The relationship field will still work as expected.

Loading related items

Filtering one-to-many related items is easy; simply specify the ID of the item you wish to filter on like any other value:

Post.model.find().where('author', author.id).exec(function(err, posts) {
    // ...
});

To filter many-to-many related items, use an in condition and specify one (or more) ids as an array:

Post.model.find().where('categories').in([category.id]).exec(function(err, posts) {
    // ...
});

Field Types

Boolean

Boolean — Displayed as a checkbox in the Admin UI

{ type: Types.Boolean }

Code

String — Displayed with CodeMirror in the Admin UI.

{ type: Types.Code, height: 180, language: json }
Options

height Number - the height of the field (in pixels). Default: 180

language String - the language in the code field.

Supported Languages: c++, objectivec, css, asp, c, vb, xml, php, html, ini, js, java, coffee, lisp, perl, python, sql, json, less, sass, sh, ruby, jsp, tpl, jade

Color

Color — Displayed as a text field with a color picker

{ type: Types.Color }

Date

Date — Displayed as a date picker in the Admin UI

Input should either be a valid Date, or a string in the format YYYY-MM-DD (can be blank unless field is required)

To default Date fields to the current time, set the default option to Date.now

{ type: Types.Date }
Options

format string - the default format pattern to use, defaults to Do MMM YYYY

yearRange Array minYear, maxYear - the default range of years to be displayed.

See the momentjs format docs for information on the supported formats and options.

Underscore methods

format(string) - formats the stored value using momentjs

moment() - returns a momentjs object initialised with the value of the field

parse(input, format, ...) - parses input using momentjs, sets the field value and returns the moment object

See the momentjs parse docs for information on the supported formats and options for the parse method.

item.createdDate = Date.now();
item._.createdDate.format(); // returns today's date using the default format string
item._.createdDate.parse('2013-12-04'); // returns a moment object with the parsed date
item._.createdDate.format('YYYY-MM-DD'); // returns '2013-12-04'

Datetime

Datetime — Displayed as a date and time picker in the Admin UI

Input should either be a valid Date, or a string in the format YYYY-MM-DD (can be blank unless field is required)

To default Date fields to the current time, set the default option to Date.now

{ type: Types.Datetime, default: Date.now }
Options:

format string - the default format pattern to use, defaults to Do MMM YYYY hh:mm:ss a

See the momentjs format docs for information on the supported formats and options.

Underscore methods:

format(string) - formats the stored value using momentjs

moment() - returns a momentjs object initialised with the value of the field

parse(input, format, ...) - parses input using momentjs, sets the field value and returns the moment object

See the momentjs parse docs for information on the supported formats and options for the parse method.

Email

String — Displayed as a text field in the Admin UI

Input must look like a valid email address (can be blank unless field is required)

{ type: Types.Email, displayGravatar: true }
Options

displayGravatar Boolean - whether to display a gravatar image in the Admin UI

Underscore methods:

gravatarUrl(input, size, defaultImage, rating) - generates a gravatar image request url

item.email = "demo@keystonejs.com";
item._.email.gravatarUrl(); // "//www.gravatar.com/avatar/74a0071e5f3a7107b570b7d4a1a7619d?s=80&d=identicon&r=g"
item._.email.gravatarUrl(200,'mm','r'); // "//www.gravatar.com/avatar/74a0071e5f3a7107b570b7d4a1a7619d?s=200&d=mm&r=r"

Html

String — Displayed as a text field or WYSIWYG Editor in the Admin UI.

{ type: Types.Html, wysiwyg: true }
Options

wysiwyg Boolean - whether to display a WYSIWYG editor in the Admin UI - for customizations of the editor see the Admin UI Options.

height Number - the height of the field (in pixels)

See the Admin UI Options for global configuration options that can be used to customise the WYSIWYG editor.

Key

String — Displayed as a text field in the Admin UI

Automatically converts input to a valid key (no spaces or special characters). White space is replaced with a separator.

{ type: Types.Key }
Options

separator String - the separator to use when replace white space in the input; defaults to -

Location

Object — Displayed as a combination of fields in the Admin UI

Contains a standard set of strings for storing an address, and a longitude / latitude point with a 2dsphere index.

Also provides autocomplete functionality using Google's Places API (requires a Google Maps API Key to be provided, must only be used in accordance with Google's terms of service).

Google Places integration requires the google api key option to be set for Keystone. See the Google configuration documentation for details.

{ type: Types.Location, defaults: { country: 'Australia' } }

Note: the schema paths are based on Australian address formats, and should be updated to be more appropriate for other international formats. If you have feedback on how the structure should be internationalised, please open a ticket.

Options

defaults Object - default values for each path in the field schema

Schema

name String - building name

number String - unit or shop number

street1 String - street address

street2 String - street address line 2

suburb String

state String

postcode String

country String

geo Array longitude, latitude

Important: as per the MongoDB convention, the order for the geo array must be lng, lat which is the opposite of the order used by Google's API.

Underscore methods

googleLookup(region, update, callback) - autodetect the full address and lng, lat from the stored value.

  • region String is passed to the Places API for regional biasing and filtering.
  • update String passing "overwrite" will completely overwrite existing data with the result. true will set blank properties on the field with the result.
  • callback(err, location, result) - is passed the parsed location object, and the raw result from Google.

Internal status codes mimic the Google API status codes. See https://developers.google.com/maps/documentation/geocoding/ for more information.

Use of the Google Geocoding API is subject to a query limit of 2,500 geolocation requests per day, except with an enterprise license.

The Geocoding API may only be used in conjunction with a Google map; geocoding results without displaying them on a map is prohibited. Please make sure your Keystone app complies with the Google Maps API License.

Markdown

Object — Displayed as a textarea field in the Admin UI

{ type: Types.Markdown }
Options

height Number - defines the height of the markdown editor; defaults to 90.

{ type: Types.Markdown, height: 200 }

toolbarOptions Object - allow customizations of the toolbar.

toolbarOptions.hiddenButtons String - Comma separated list of buttons to hide.

{ type: Types.Markdown, toolbarOptions: { hiddenButtons: 'H1,H6,Code' } }
Schema

The markdown field will automatically convert markdown to html when the md property is changed, via a setter on the md path.

md String - source markdown text

html String - generated html code

Page.add({ content: Types.Markdown });
 
var page = new Page.model();
page.content.md = "# Hello World";
page.content.html == "<h1>Hello World</h1>";
 
// or...
 
Page.fields.content.updateItem(page, "* list item");
page.fields.content.format(page) == "<ul><li>list item</ul></li>";

Money

Number — Displayed as a number field in the Admin UI

Input should either be a valid Number, or a string that can be converted to a number (leading symbols are allowed; can be blank unless field is required).

{ type: Types.Money }
Options

format String, formats the stored value using numeraljs.

{ type: Types.Money, format: '$0,0.00' }

currency String, loads a predefined object of settings for a specific language, the language must exist as a .js in numeral/languages folder.

{ type: Types.Money, currency: 'en-gb' }
Underscore methods

format(string) - formats the stored value using numeraljs. Set to false to disable automatic formatting.

Format string defaults to $0,0.00

Name

Object — Displayed as firstname lastname fields in the Admin UI

{ type: Types.Name }
Schema

The name field adds first and last String paths to the schema, as well as a full virtual getter and setter.

first String - first name

last String - last name

Virtuals

full String - first and last name, concatenated with a space (if both have a value)

The name.full setter splits input at the first space.

Number

Number — Displayed as a number field in the Admin UI

Input should either be a valid Number, or a string that can be converted to a number (can be blank unless field is required)

{ type: Types.Number }
Underscore methods

format(string) - formats the stored value using numeraljs. Set to false to disable automatic formatting.

Format string defaults to 0,0[.][000000000000]

Password

String — Displayed as a password field in the Admin UI, with a 'change' button.

Passwords are automatically encrypted with bcrypt, and expose a method to compare a string to the encrypted hash.

The encryption happens with a pre-save hook added to the schema, so passwords set will not be encrypted until an item has been saved to the database.

{ type: Types.Password }
Options

workFactor Number - the bcrypt workfactor to use when generating the hash, higher numbers are slower but more secure (defaults to 10)

Underscore methods

compare(candidate, callback) - encrypts the candidate and compares it against the encrypted hash

  • candidate String to compare
  • callback(err, result) - result is true if the candidate matches the stored password, or false if it doesn't
Special paths

{path}_compare - when provided to the updateHandler, it will be checked against {path} and validation will fail if they don't match.

Select

String or Number — Displayed as a select field in the Admin UI

Similar to Enum in other Frameworks.

{ type: Types.Select, options: 'first, second, third' }
Options

numeric Boolean when true, causes the value of the field to be stored as a Number instead of a String

{ type: Types.Select, numeric: true, options: [{ value: 1, label: 'One' }, { value: 2, label: 'Two' }] }

emptyOption Boolean when undefined || true, includes a blank value as the first option in the <select> field.

{ type: Types.Select, required: true, options: 'first, second', emptyOption: false }

options String or Array - the options for the select field

Option values can be provided as a comma-delimited list String of values, in which the string is split into an Array.

For an Array of options, each option should be either a

  • String representing the value of the option; the label is automatically generated
  • Object with value and label String properties

You can mix String and Object items in the options Array:

{ type: Types.Select, options: ['first', 'second', { value: 'third', label: 'The third one' }] }

Object options can have additional properties which are accessible when the current options data, or fields options are retrieved.

{ type: Types.Select, options: [
    { value: 'first', label: 'The first option', custom: 'value' },
    { value: 'second', label: 'Second' }
]}
Properties

ops Array - the field options array

values Array - all option.value properties

labels Object - all option.label properties, keyed by option.value

map Object - map of options, keyed by option.value

Schema

The value of the current option will be stored at {path}. In addition, these virtuals are provided:

pathLabel String - the label of the currently selected option

pathData Object - the currently selected option, including any custom properties

pathOptions Array - the field options array

pathOptionsMap Object - map of options, keyed by option.value

Underscore methods:

pluck(property, default) - returns property value of the currently selected option, or default. Useful in conjunction with custom properties for options.

MyList.add({ state: { type: Types.Select, options: 'draft, published, archived', default: 'draft' });
 
MyList.fields.state.values == 'draft,published,archived';
MyList.fields.state.labels == { draft: 'Draft', published: 'Published', archived: 'Archived' };
MyList.fields.state.ops == [
    { value: 'draft', label: 'Draft' },
    { value: 'published', label: 'Published' },
    { value: 'archived', label: 'Archived' }
];
MyList.fields.state.map == {
    draft: { value: 'draft', label: 'Draft' },
    published: { value: 'published', label: 'Published' },
    archived: { value: 'archived', label: 'Archived' }
};
 
var item = new MyList.model();
item.state == 'draft';
item.stateLabel == 'Draft';
item.stateData == { value: 'draft', label: 'Draft' };
item.stateOptions == MyList.fields.state.ops;
item.stateOptionsMap == MyList.fields.state.map;

Text

String — Displayed as a text field in the Admin UI

{ type: Types.Text }

Textarea

String — Displayed as a textarea field in the Admin UI

{ type: Types.Textarea }
Options

height Number - the height of the field (in pixels)

Url

String — Displayed as a text field in the Admin UI.

{ type: Types.Url }
Options

format Function - process URL before its printed in the href/text of Admin UI cell. Defaults to removeProtocolPrefix

Underscore methods:

format() - formats the stored value by stripping the leading protocol (if any)

item.url = "https://keystonejs.com";
item._.url.format(); // "keystonejs.com"

Storage

AzureFile

Object — Displayed as an file upload field in the Admin UI

Automatically manages files stored in Windows Azure Storage, including uploading and deleting.

{ type: Types.AzureFile }
Options

filenameFormatter Callback - function with arguments current model and client file name to return the new filename to upload.

{ type: Types.AzureFile, filenameFormatter: function(item, filename) {
	return item._id + require('path').extname(filename);
} }

containerFormatter Callback - function with arguments current model and client file name to return the new container name (container are a root folder in Azure Storage Account).

{ type: Types.AzureFile, containerFormatter: containerFormatter: function(item, filename) {
	return item.modelProperty;
} }
Schema

filename String

type String

filesize Number

url String

etag String

Virtuals
exists Boolean - whether there is a stored file
Underscore methods

uploadFile(file, update, callback) - uploads a file to the Azure Storage Account, stores the details in the field and provides the file data to the callback.

  • file File should be a file as provided by express when a file is uploaded, i.e. req.files.path
  • update Boolean whether to update the field with the details of the file after upload completes
  • callback(err, fileData) - is passed the object that will be stored in the field (see schema above)

CloudinaryImage

Object — Displayed as an image upload field in the Admin UI

Automatically manages images stored in Cloudinary, including uploading, resizing and deleting.

See the Cloudinary configuration documentation for details on how to set up Cloudinary in KeystoneJS.

{ type: Types.CloudinaryImage }
Options

publicID String name of the field to be used as the Cloudinary image public_id.

{ type: Types.CloudinaryImage, publicID: 'slug' }

folder String specifies a custom folder/prefix for the Cloudinary image public_id when cloudinary folders is set to true.

{ type: Types.CloudinaryImage, folder: 'path/to/image' }

If you want Cloudinary to automatically create folders when the cloudinary folders option is set to true, make sure you enable "Auto-create folders" in your Cloudinary account "Upload Settings".

autoCleanup Boolean when true changes Keystone's default behavior from remove (which only removes the Cloudinary image from the database) to delete (which removes the image from both the database and Cloudinary storage). Additionally, this option replaces an existing image (if one already exists) during upload.

{ type: Types.CloudinaryImage, autoCleanup : true }

select Boolean when true a select field is displayed with a list of images currently available in Cloudinary storage. Only images with IDs that begin with the selectPrefix will be displayed when the selectPrefix is specified. Otherwise, images with IDs that begin with the folder will be displayed. If neither selectPrefix nor folder are configured, then only images with IDs that begin with [{prefix}]/{list.path}/{field.path}/ will be displayed.

{ type: Types.CloudinaryImage, select : true }

selectPrefix String specifies the the prefix of the images that will be available for selection when select option is true.

{ type: Types.CloudinaryImage, select: true, selectPrefix: 'path/to/images' }
Schema

public_id String

version Number

signature String

format String

resource_type String

url String

width Number

height Number

secure_url String

Virtuals
exists Boolean - whether there is a stored image
Special paths

{path}_upload - when a file is provided to the updateHandler, it will be uploaded to cloudinary and the details will be stored in the field.

Underscore methods

src(options) String - returns the url of the image, accepts all options cloudinary supports

tag(options) String - returns an <img> tag

scale(width, height, options) String - scales the image to fit the exact width and height, retaining aspect ratio

fit(width, height, options) String - scales the image to fit within the specified width and height, retaining aspect ratio

lfit(width, height, options) String - scales the image to fit within the specified width and height, retaining aspect ratio (without exceeding the original dimensions)

limit(width, height, options) String - scales the image (down only) to fit within the specified width and height, retaining aspect ratio

fill(width, height, options) String - scales the image to fill the specified width and height

crop(width, height, options) String - crops the image to fill the specified width and height

pad(width, height, options) String - pads the image to fill the specified width and height

lpad(width, height, options) String - pads the image to fill the specified width and height (without exceeding the original dimensions)

thumbnail(width, height, options) String - crops the image to fill the specified width and height

In all methods, options is an optional Object. See Cloudinary's Transformation Documentation for more information on the supported options and transformations.

Remember that if you are uploading images to a CloudinaryImage field using an HTML form, you need to specify enctype="multipart/form-data" in your form tag.

CloudinaryImages

Array — Displayed as a series of images, and an upload field in the Admin UI

Stores multiple images in a array as a nested Schema, each of which expose the same methods as the cloudinaryimage field.

{ type: Types.CloudinaryImages }
Options

folder String specifies a custom folder/prefix for the Cloudinary image public_id when cloudinary folders is set to true.

{ type: Types.CloudinaryImages, folder: 'path/to/image' }

If you want Cloudinary to automatically create folders when the cloudinary folders option is set to true, make sure you enable "Auto-create folders" in your Cloudinary account "Upload Settings".

Embedly

Object — Displayed as read-only data in the Admin UI

Automatically retrieves data from the Embedly API about the value of another field (specified with the from option).

It stores the retrieved data (which includes the provider, media type, full URL, HTML embed code, width, height, thumbnail picture and more).

The api call to retrieve the data is implemented as a pre-save hook, and is only triggered if the from path value has changed.

See the Embed.ly configuration documentation for details on how to set up Embed.ly in KeystoneJS.

{ type: Types.Embedly, from: 'path' }
Options

from String - the path of another field in the Schema that will be passed to the Embedly API. The other field must contain a String value.

options Object (optional) - passed as arguments to the embedly API along with the from field value

See Embedly's oEmbed API documentation for more information on options and returned data.

Schema

exists Boolean

type String

title String

url String

width Number

height Number

version String

description String

html String

authorName String

authorUrl String

providerName String

providerUrl String

thumbnailUrl String

thumbnailWidth Number

thumbnailHeight Number

LocalFile

This field type is not compatible with PAAS Hosts like Heroku because it relies on the local file system

Object — Displayed as a file upload field in the Admin UI

Stores files on the local file system.

{ type: Types.LocalFile }
Options

dest String - required, the path to store uploaded file

prefix String - the path prefix in browser, if it different with dest

datePrefix String - if set, prefixes the file name with the current date in this format (see moment.js for format options)

allowedTypes Array of String - optional white-list of allowed mime types for uploaded file

filename Function - function with two arguments: current model and file object to return the new filename to upload.

{
	type: Types.LocalFile,
	dest: '/data/files',
	prefix: '/files/',
	filename: function(item, file){
		return item.id + '.' + file.extension
	}
}

format Function - function with two arguments: current model and file object to return representation of this file in Admin UI.

{
	type: Types.LocalFile,
	dest: '/data/files',
	prefix: '/files/',
	format: function(item, file){
		return '<img src="/files/'+file.filename+'" style="max-width: 300px">'
	}
}

Schema

filename String

path String

size Number

filetype String

Virtuals
exists Boolean - whether there is a file path stored
Underscore methods

uploadFile(file, update, callback) - uploads a file to the local storage, stores the details in the field and provides the file data to the callback.

  • file File should be a file as provided by express when a file is uploaded, i.e. req.files.path
  • update Boolean whether to update the field with the details of the file after upload completes
  • callback(err, fileData) - is passed the object that will be stored in the field (see schema above)

S3 File

Object — Displayed as an file upload field in the Admin UI

Automatically manages files stored in Amazon S3, including uploading and deleting.

{ type: Types.S3File }
Options

s3path String - the path to store uploaded files under in the S3 bucket

datePrefix String - if set, prefixes the file name with the current date in this format (see moment.js for format options)

allowedTypes Array of String - optional white-list of allowed mime types for uploaded files

filename Function - function with arguments current model and client file name to return the new filename to upload.

{
	type: Types.S3File,
	filename: function(item, filename){
		// prefix file name with object id
		return item._id + '-' + filename;
	}
}

headers Object or Array or Function - headers to set on the S3 object

Headers can be provided as an Object, in which the keys are used as header names and the values are used as header values.

{ 
	type: Types.S3File, 
	headers: { 
		'x-amz-meta-Cache-Control' : 'max-age=' + (60 * 15),
		'x-amz-meta-X-Custom-Header' : 'Object Option'
	} 
}

When the option is provided as an Array of headers, each header element in the array should be an Object with name and value String properties

{ 
	type: Types.S3File, 
	headers: [
		{ name: 'x-amz-meta-Cache-Control', value: 'max-age=' + (60 * 15) },
		{ name: 'x-amz-meta-X-Custom-Header', value: 'Array Option' }
	]
}

When the option is provided as a Function, the function will be called with arguments for the current model and client file; The function should return either an Array of valid header objects or a simple Object.

{ 
	type: Types.S3File, 
	headers: function (item, file) {
		var headers = [];
		headers.push({ name: 'x-amz-meta-Cache-Control', value: 'max-age=' + item.maxAge });
		headers.push({ name: 'x-amz-meta-X-Custom-Header', value: 'Computed Option (Array)' });
		return headers;
	}
}
// or 
{ 
	type: Types.S3File, 
	headers: function (item, file){
		var headers = {};
		headers['x-amz-meta-Cache-Control'] = 'max-age=' + item.maxAge;
		headers['x-amz-meta-X-Custom-Header'] = 'Computed Option (Object)';
		return headers;
	}
}

format Function - function with two arguments: current model and file object to return representation of this file in Admin UI.

{
	type: Types.S3File,
	format: function(item, file){
		return '<pre>'+JSON.stringify(file, false, 2)+'</pre>'+
					'<img src="'+file.url+'" style="max-width: 300px">'
	}
}

Schema

filename String

type String

filesize Number

url String

Virtuals
exists Boolean - whether there is a stored file
Special paths

{path}_upload - when a file is provided to the updateHandler, it will be uploaded to s3 and the details will be stored in the field.

Underscore methods

uploadFile(file, update, callback) - uploads a file to the s3 bucket, stores the details in the field and provides the file data to the callback.

  • file File should be a file as provided by express when a file is uploaded, i.e. req.files.path
  • update Boolean whether to update the field with the details of the file after upload completes
  • callback(err, fileData) - is passed the object that will be stored in the field (see schema above)

More examples

See the Examples page for projects that demonstrate real-world usage of the various list options and field types.