Edit this page

Upgrade Guide: 0.3 to 4.0

Changes to express application and mounting

WIP

The mounting process has been rewritten for modularity and clarity. This may introduce some breaking changes if your app has been attaching the Keystone Admin UI to a nonstandard express app (i.e. not using Keystone.start(), but should make things simpler going forward.

Changed

  • keystone.get('express session') has been replaced by keystone.expressSession
  • Session initialisation middleware is now executed before the body parser to allow for the Admin UI to include its own bodyParser implementation
  • IP Restrictions middleware is now processed first, which means that static assets will now respect restrictions (previously this only restricted routes)
  • keystone.routes(fn) has been replaced by keystone.set('routes', fn)
  • the routes option supports both:

    • the legacy function (app) {} way of adding routes to the root express app
    • an express.Router instance
  • keystone.set('pre:routes') now runs the hook as middleware before each route is called, rather than running the function before Express starts up.

Removed

  • keystone.static has been removed (replacement TBA, see admin/server)
  • keystone.mount has been removed (replaced by more granular methods, see admin/server)
  • keystone.bindEmailTestRoutes and keystone.set('email tests') support (was this actually being used by anyone? let me know - @jedwatson)
  • keystone.set('email rules') option has been removed; you should just use variables in your templates, rather than globally applied regular expressions.
  • The Types.CloudinaryImage option publicID: 'slug' option has been removed. It has been replaced by the generateFilename function. Documentation for the new option can be found in fields/types/cloudinaryimage

UpdateHandler Changes

The UpdateHandler functionality has been completely rewritten to use the new, generic List.updateItem method. While we have tried to preserve backwards compatibility, there may be minor differences in functionality and the format of messages returned.

The structure of error detail passed to the callback has changed, and now matches the new API error format (see the API Spec)

If you are using the UpdateHandler please test your application comprehensively when upgrading to 0.4 and report any problems you find in the issues.

Specific changes include:

  • The callback function now receives a single error argument. To retreive the created/updated object use:
// To create a new item
var model = new MyModel.model();
MyModel.model().updateItem(model, data, options, function (err) {
    if (err) {
        // Error handling
    } else {
        var savedItem = MyModel.getData(model);
    }
});
  • options.validationMethods is no longer supported, please apply any custom validation before calling updateHandler.process()
  • options.errorMessages is no longer supported, as fields may now return different error conditions and replacing specific error detail with a simple string has the potential to be misleading and frustrating for users. If you want to show custom messages, please handle this on a case-by-case basis in your application.

The flashErrors option now supports the following values:

  • true send all error messages to req.flash
  • "validation" only send validation error messages to req.flash
  • "update" only send update errors to req.flash

File handling Field Types

New File field type

The new File field type, with the FS, S3 and Azure storage adapters, replace the following field types (which are removed in 0.4) -

  • azureFile
  • localFile
  • localFiles
  • s3File

For usage instructions, see:

For the full upgrade guide for File fields, see File-Fields-Upgrade-Guide in the KeystoneJS Wiki.

CloudinaryImage

The _upload suffix is no longer required or supported when uploading files from an html form. Now simply provide the cloudinary field path as the name of file upload fields. For example:

<!-- old -->
<input type="file" name="picture_upload" />
<!-- new -->
<input type="file" name="picture" />

CloudinaryImages

When the cloudinary folders Keystone option is set, in 0.3.x CloudinaryImages fields would incorrectly use the cloudinary prefix option's value as the first part of the folder path for fields that did not specify the folder option. This has been fixed; the folder for uploaded images now defaults to this.list.path + '/' + this.path

Explicit support for actions with paths.action (i.e. submitting a fieldPath_action value) has been removed. To remove images from the value, submit the new value for the field without the removed image present.

The CloudinaryImages field no longer supports deleting removed images from Cloudinary, this may be restored in a subsequent version.

Reordering images with paths.order (i.e. submitting a fieldPath_order value) has also been removed. To sort images, submit the full array of values in the correct order.

Field API Changes

  • updateItem is now asynchronous and need to pass a callback:

    • Before: field.updateItem(item, data)
    • After: field.updateItem(item, data, function(err) {...})

Location fields

The geocodeGoogle option has been renamed to enableImprove.

Mongoose 4

The bundled version of Mongoose has been updated from 3.x to 4.x. Please review the Migration Guide and Release Notes for more information.

Keystone.Email

Keystone.Email was completely rewritten as the standalone, well-tested keystone-email module, and requires it to be installed to work.

Changed

  • new Email now accepts a second argument as the options (e.g. to set the transport)
new Email('templatepath.ext', { transport: 'mailgun' });
  • The template locals are now passed in as the first, the options for send as the second and the callback as the third argument to .send
new Email('templatepath.ext', { transport: 'mailgun' })
  .send(templateLocals, sendOptions, callback);

Removed

These options, APIs or behaviours were removed with the rewrite to an external module.

  • We no longer assume email.jade as the default filename, you now have to pass an entire filename to or set the engine in the .Email call

Deprecated

All of these deprecations will continue to work throughout v4.x, though warnings will be added in a future minor release. We highly encourage moving to the new APIs as soon as possible, your apps will break in v5 otherwise!

  • keystone.set('email') has been deprecated in favor of the root option (passed to the .Email call)
  • The templateExt option was deprecated in favor of the engine option
  • keystone.set('transport') has been deprecated in favor of setting transport in the new Email() options
  • keystone.set('mailgun api key') and keystone.set('mailgun domain') have been deprecated in favor of passing the apiKey and domain option to the send options in .send

CSV Download

Keystone 0.3 had a feature where you could specify a custom toCSV method on a list schema to control which values were included in the CSV.

As the CSV Export has been completely rewritten and now supports the same filter and fields options that the List view in the Admin UI does (including being able to specify which fields are included in the CSV from the Admin UI), the toCSV method has been replaced by a new getCSVData method:

User.schema.methods.getCSVData = function (data, options) {
  return {
    password: undefined,
    custom: { complex: true },
  };
};

Values returned by this method will be added to the CSV regardless of the fields specified to be included. You have access to the fields in options.fields. Additionally, you can set values to undefined to remove them from the csv if they were included.

The current User, if there is one, is provided in options.user.

New Features

toCSV method on Fields

You can now also specify a per-field toCSV transform. Provide a function to return a custom value for the field if it is specified in options.fields:

User.add({
  email: { type: Types.Email, toCSV: function (field, options) {
    return this.id === options.user.id ? 'Your email address' : this.email;
  }}
})

The this scope will be the document being serialised to CSV format. The field argument is the field definition, and the options argument contains the options passed to getCSVData (see above).

For field types that store an object, you can instead provide a list or array of keys to include in the CSV data:

User.add({
  address: { type: Types.Location, toCSV: 'suburb, postcode' }
})

Automatic flattening of complex values

Objects will be flattened to multiple columns, with each key being appended to the field path using camelCase.

So, for example, the following data from a File field with the path attachment:

{
  id: '56dd3f1a51cb31100cf08457',
  attachment: {
    filename: 'photo.jpg',
    size: 83452
  }
}

Will be added to the CSV in two columns:

id,attachmentFilename,attachmentFilesize
56dd3f1a51cb31100cf08457,photo.jpg,83452

Removed Options

The csv expanded Keystone option has been deprecated. Relationship fields will always be expanded into fieldName, fieldId columns by default.

Changed Dependencies

Breaking Fixes

Relationship Field Updates

Relationship fields had a bug in 0.3.x and 4.0.0 betas pre-beta.6 where undefined values passed to List.updateItem would be persisted to the database for { many: true } fields.

This has been fixed, so that incoming undefined values are ignored, which is consistent with all other field types except Boolean.

Jade

Keystone 3 included jade as a dependency; this meant your project may have been able to use jade as the view engine without depending on it explicitly. Jade has now been removed from Keystone, so if you're using it you will need to explicitly add it to your package.json.

Underscore

Keystone 3 included underscore as a dependency; this was utilised by the generator, which should have added an explicit dependency when generating package.json files. Unfortunately it didn't, which means your project may expect underscore to be available and it won't be.

If you get the following error when starting a project with Keystone 4:

Error: Cannot find module 'underscore'

Run the following command in your project's root folder to resolve it:

npm install --save underscore