Edit Page

Upgrading to Sails v1.0

Sails v1.0 is here! Keep reading for a high-level overview of what's changed in this release, and to learn about some new features you might want to take advantage of in your app.

A note about breaking changes

#

While working on this version of Sails, a lot of the decisions we made favored a better developer experience over backwards compatibility. Because of this, the upgrade to Sails 1.0 will involve dealing with more breaking changes than previous versions. But when you're finished, there'll be a much better chance that the features you're using in Sails are things that its author and maintainers understand thoroughly and use almost every day.

For more about the philosophy behind many of the breaking changes in 1.0, you can read Mike McNeil's in-depth explanation here.

Upgrading an existing app using the automated tool

#

Ready to upgrade your existing v0.12.x Sails app to version 1.0? To get started, we recommend using the Sails 1.0 upgrade tool, which will help with some of the most common migration tasks. To use the tool, first install Sails 1.0 globally with npm install -g sails@^1.0.0 and then run sails upgrade. After the tool runs, it will create a report for you with a list of remaining items that need to be manually upgraded.

Upgrading an existing app manually

#

The checklist below covers the changes most likely to affect the majority of apps.

If your app still has errors or warnings on startup after following this checklist, or if you're seeing something unexpected, head back to this document and take a look further down the page. (One of the guides for covering various app components will probably be applicable.)

We've done a lot of work to make the upgrade process as seamless as possible, particularly when it comes to the errors and warnings you'll see on the console. But if you're stumped or have lingering questions about any of the changes below, feel free to drop by the Sails community Gitter channel. (If your company is using Sails Flagship, you can also chat directly with the Sails core team here.)

tl;dr checklist: things you simply must do when upgrading to version 1.0

#

The upgrade tool does its best to help with some of these items, but it won’t change your app-specific code for you!

Step 0: Check your Node version!
#

If your app needs to support Node versions earlier than v4, you will not be able to upgrade to Sails 1.0, as Sails 1.0 no longer supports Node v0.x. The earliest version of Node supported by Sails 1.0 is Node 4.x.

Step 1: Install hooks & update dependencies
#

Sails v1 introduces custom builds. This means that certain core hooks are now installed as direct dependencies of your app, giving you more control over your dependencies and making npm install sails run considerably faster. So, the first thing you'll need to do is install the core hooks you're using. (And while you're at it, be sure to update the other dependencies mentioned in the list below.)

Step 2: Update configuration
#

Sails v1 comes with several improvements in app configuration. For example, automatic install of lodash and async can now be customized to any version, and view engine configuration syntax is now consistent with that of Express v4+. The most significant change to configuration, however, is related to one of the most exciting new features in Sails v1: datastores. To make sure you correctly upgrade the configuration for your database(s) and other settings, be sure to carefully read through the steps below and apply the necessary changes.

Step 3: Modify client-side code for the new blueprint API
#

As well as having been expanded to include a new endpoint, there also are a couple of minor—but breaking—changes to the blueprint API that may require you to make changes to your client-side code.

Step 4: Adopt the new release of Waterline ORM
#

The new release of Waterline ORM (v0.13) introduces full support for SQL transactions, the ability to include or omit attributes in result sets (aka "projections"), dynamic database connections, and more extensive granular control over query behavior. It also includes a major stability and performance overhaul, which comes with a few breaking changes to usage. The bullet points below cover the most common issues you're likely to run into with the Waterline upgrade.

Other breaking changes

#

The upgrade guide above provides for the most common upgrade issues that Sails contributors have encountered when upgrading various apps between version 0.12 and version 1.0. Every app is different, though, so we recommend reading through the points below, as well. Not all of the changes discussed will necessarily apply to your app, but some might.

Changes to database configuration

#

Nested creates and updates

#

Changes to model configuration

#
tl;dr
#

Remove any autoPK, autoCreatedAt and autoUpdatedAt properties from your models, and add the following to your config/models.js file:

attributes: {
    createdAt: { type: 'number', autoCreatedAt: true, },
    updatedAt: { type: 'number', autoUpdatedAt: true, },
    id: { type: 'number', autoIncrement: true}, // <-- for SQL databases
    id: { type: 'string', columnName: '_id'}, // <-- for MongoDB
  }
The autoPK top-level property is no longer supported
#

This property was formerly used to indicate whether or not Waterline should create an id attribute as the primary key for a model. Starting with Sails v1.0 / Waterline 0.13, Waterline will no longer create any attributes in the background. Instead, the id attribute must be defined explicitly. There is also a new top-level model property called primaryKey, which can be set to the name of the attribute that should be used as the model's primary key. This value defaults to id for every model, so in general you won't have to set it yourself.

The autoUpdatedAt and autoCreatedAt model settings are now attribute-level properties
#

These properties were formerly used to indicate whether or not Waterline should create createdAt and updatedAt timestamps for a model. Starting with Sails v1.0 / Waterline 0.13, Waterline will no longer create these attributes in the background. Instead, the createdAt and updatedAt attributes must be defined explicitly if you want to use them. By adding autoCreatedAt: true or autoUpdatedAt: true to an attribute definition, you can instruct Waterline to set that attribute to the current timestamp whenever a record is created or updated. Depending on the type of these attributes, the timestamps will be generated in one of two formats:

Furthermore, for any attribute, if you pass new Date() as a constraint within a Waterline criteria's where clause, or as a new record, or within the values to set in a .update() query, then these same rules are applied based on the type of the attribute. If the attribute is type: 'json', it uses the latter approach.

Changes to .create(), .createEach(), .update(), and .destroy() results

#

As of Sails v1.0 / Waterline 0.13, the default result from .create(), .createEach(), .update(), and .destroy() has changed.

To encourage better performance and easier scalability, .create() no longer sends back the created record. Similarly, .createEach() no longer sends back an array of created records, .update() no longer sends back an array of updated records, and .destroy() no longer sends back destroyed records. Instead, the second argument to the .exec() callback is now undefined (or the first argument to .then(), if you're using promises).

This makes your app more efficient by removing unnecessary find queries, and it makes it possible to use .update() and .destroy() to modify many different records in large datasets, rather than falling back to lower-level native queries.

You can still instruct the adapter to send back created or modified records for a single query by using the fetch method. For example:

Article.update({
  category: 'health-and-wellness',
  status: 'draft'
})
.set({
  status: 'live'
})
.fetch()
.exec(function(err, updatedRecords){
  //...
});

If the prospect of changing all of your app's queries seems daunting, there is a temporary convenience you might want to take advantage of. To ease the process of upgrading an existing app, you can tell Sails/Waterline to fetch created/updated/destroyed records for ALL of your app's .create()/.createEach()/.update()/.destroy() queries. Just edit your app-wide model settings in config/models.js:

fetchRecordsOnUpdate: true,
fetchRecordsOnDestroy: true,
fetchRecordsOnCreate: true,
fetchRecordsOnCreateEach: true,

That's it! Still, to improve performance and future-proof your app, you should go through all of your .create(), .createEach(), .update(), and .destroy() calls and add .fetch() when you can. Support for these model settings will eventually be removed in Sails v2.

Changes to Waterline criteria usage

#
Change in support for mixed where clauses
#

Criteria dictionaries with a mixed where clause are no longer supported. For example, instead of:

{
  username: 'santaclaus',
  limit: 4,
  select: ['beardLength', 'lat', 'long']
}

you should use:

{
  where: { username: 'santaclaus' },
  limit: 4,
  select: ['beardLength', 'lat', 'long']
}

Note that you can still do { username: 'santaclaus' } as shorthand for { where: { username: 'santaclaus' } }, you just can't mix other top-level criteria clauses (like limit) alongside constraints (e.g. username).

For places where you're using Waterline's chainable deferred object to build criteria, don't worry about this—it's already taken care of for you.

Security

#

New apps created with Sails 1.0 will contain a config/security.js file instead of individual config/cors.js and config/csrf.js files. Apps migrating from earlier versions can keep their existing files, as long as they perform the following upgrades:

'POST /some-thing': { action: 'do-a-thing', csrf: false },
'GET /csrfToken': {
  action: 'security/grant-csrf-token',
  cors: {
    allowOrigins: ['http://foobar.com', 'https://owlhoot.com']
  }
}

Views

#

For maximum flexibility, Consolidate is no longer bundled with Sails. If you are using a view engine besides EJS, you'll probably want to install Consolidate as a direct dependency of your app. You can then configure the view engine in config/views.js, like so:

extension: 'swig',
getRenderFn: function() {
  // Import `consolidate`.
  var cons = require('consolidate');
  // Return the rendering function for Swig.
  return cons.swig;
}

Adding custom configuration to your view engine is a lot easier in Sails 1.0:

extension: 'swig',
getRenderFn: function() {
  // Import `consolidate`.
  var cons = require('consolidate');
  // Import `swig`.
  var swig = require('swig');
  // Configure `swig`.
  swig.setDefaults({tagControls: ['{?', '?}']});
  // Set the module that Consolidate uses for Swig.
  cons.requires.swig = swig;
  // Return the rendering function for Swig.
  return cons.swig;
}

Note that the built-in support for layouts still works for the default EJS views, but layout support for other view engines (e.g. Handlebars or Ractive) is not bundled with Sails 1.0.

Resourceful PubSub

#

In place of the removed methods, you should use the new .publish() method, or the low-level sails.sockets methods. Keep in mind that unlike .message(), .publish() does not wrap your data in an envelope containing the record ID, so—if it's important—you'll need to include the ID yourself as part of the data. For example, in Sails v0.12.x, User.message(123, {owl: 'hoot'}) would have resulted in the following notification being broadcasted to clients:

{
  verb: 'messaged',
  id: 123,
  data: {
    owl: 'hoot'
  }
}

By contrast, in Sails v1.0, User.publish(123, {owl: 'hoot'}) will simply broadcast:

{
  owl: 'hoot'
}

Replacing custom blueprints

#

Out of the box, it is no longer possible to add a file to api/blueprints/ that will automatically be used as a blueprint action for all models. However, this behavior can easily be replicated by installing sails-hook-custom-blueprints.

Express 4

#

Sails 1.0 comes with an update to the internal Express server from version 3 to version 4 (thanks to some great work by @josebaseba). This change is mainly about maintainability for the Sails framework and should be transparent to your app. However, there are a couple of differences worth noting:

customMiddleware: function(app) {
  var passport = require('passport');
  app.use(passport.initialize());
  app.use(passport.session());
}

with something like:

var passport = require('passport');
middleware: {
  passportInit: passport.initialize(),
  passportSession: passport.session()
},

being sure to insert passportInit and passportSession into your middleware.order array in config/http.js.

Response methods

#

i18n

#

Sails 1.0 switches from using the i18n to the lighter-weight i18n-2 module. The overwhelming majority of users should see no difference in their apps. However, if you’re using the sails.config.i18n.updateFiles option, be aware that this is no longer supported; instead, locale files will always be updated in development mode, and never in production mode. If this is a problem or you’re missing some other feature from the i18n module, you can install sails-hook-i18n to revert to pre-Sails-1.0 functionality.

If your 0.12 application is running into issues during upgrade due to its use of i18n features, see #4343 for more troubleshooting tips.

WebSockets

#

All Sails 1.0 projects that use websockets must install the latest sails-hook-sockets dependency (npm install --save sails-hook-sockets). This version of sails-hook-sockets differs from previous ones in a couple of ways:

Grunt

#

The Grunt task-management functionality that was formerly part of the Sails core has now been moved into the separate sails-hook-grunt module. Existing apps simply need to npm install --save sails-hook-grunt to continue using Grunt. However, with a modification to your app’s Gruntfile.js, you can take advantage of the fact that sails-hook-grunt includes all of the grunt-contrib modules that previously had to be installed at the project level. The new Gruntfile.js contains:

module.exports = function(grunt) {

  var loadGruntTasks = require('sails-hook-grunt/accessible/load-grunt-tasks');

  // Load Grunt task configurations (from `tasks/config/`) and Grunt
  // task registrations (from `tasks/register/`).
  loadGruntTasks(__dirname, grunt);

};

Assuming that you haven’t customized the Gruntfile in your app, you can replace Gruntfile.js with that code and then safely run:

npm uninstall --save grunt-contrib-clean
npm uninstall --save grunt-contrib-coffee
npm uninstall --save grunt-contrib-concat
npm uninstall --save grunt-contrib-copy
npm uninstall --save grunt-contrib-cssmin
npm uninstall --save grunt-contrib-jst
npm uninstall --save grunt-contrib-less
npm uninstall --save grunt-contrib-uglify
npm uninstall --save grunt-contrib-watch
npm uninstall --save grunt-sails-linker
npm uninstall --save grunt-sync
npm uninstall --save grunt-cli

to remove those dependencies from your project.

Troubleshooting

#
Still displaying v0.12 at launch?
#

Make sure you have sails installed locally in your project, and that you're using the v1 version of the command-line tool.

To install the v1.0 globally, run npm install sails@^1.0.0 -g. To install it for a particular Sails app, cd into that app's directory, then run npm install sails@^1.0.0 --save. (After installing locally, be sure to also install the necessary hooks -- see above.)

Need help upgrading?

If your company has the budget, consider purchasing Flagship support. It's a great way to support the ongoing development of the open source tools you use every day. And it gives you an extra lifeline to the Sails core team.

Upgrading