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 [email protected]^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

: Check your Node version Step 1 : Install hooks & update dependencies

: Install hooks & update dependencies Step 2 : Update configuration

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

: Modify client-side code for the new blueprint API Step 4: Adopt the new release of Waterline ORM

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.

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.)

Install the sails-hook-orm package into your app with npm install --save sails-hook-orm , unless your app has the ORM hook disabled.

into your app with , unless your app has the ORM hook disabled. Install the sails-hook-sockets package into your app with npm install --save sails-hook-sockets , unless your app has the sockets hook disabled.

into your app with , unless your app has the sockets hook disabled. Install the sails-hook-grunt package into your app with npm install --save sails-hook-grunt , unless your app has the Grunt hook disabled.

into your app with , unless your app has the Grunt hook disabled. Install the latest version of your database adapter . For example, if you're using sails-mysql , do npm install --save [email protected] .

. For example, if you're using , do . Upgrade your sails.io.js websocket client with sails generate sails.io.js . See the "Websockets" section below for more details.

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.

Update your config/globals.js file (unless your app has sails.config.globals set to false ) Set models and sails to have boolean values ( true or false ). Set async and lodash to either have require('async') and require('lodash') respectively, or else false . You may need to npm install --save lodash and npm install --save async , as well.

(unless your app has set to ) Comment out any database configuration your aren’t using in config/connections.js . Unlike previous versions, Sails 1.0 will load all database adapters that are referenced in config files, regardless of whether they are actually used by a model. See the migration guide section on database configuration for more info.

in . Unlike previous versions, Sails 1.0 will load all database adapters that are referenced in config files, regardless of whether they are actually used by a model. See the migration guide section on database configuration for more info. The /csrfToken route is no longer provided to all apps by default when using CSRF. If you're utilizing this route in your app, you'll need to manually add it to config/routes.js as 'GET /csrfToken': { action: 'security/grant-csrf-token' } .

is no longer provided to all apps by default when using CSRF. If you're utilizing this route in your app, you'll need to manually add it to as . If your app relies on action shadow routes (where every custom controller action is automatically mapped to a route), you’ll need to update your config/blueprints.js file and set actions to true . This setting is now false by default.

(where every custom controller action is automatically mapped to a route), you’ll need to update your file and set to . This setting is now by default. If your app uses CoffeeScript or TypeScript see the CoffeeScript and TypeScript tutorials for update information.

see the CoffeeScript and TypeScript tutorials for update information. If your app uses a view engine other than EJS , you’ll need to configure it yourself in the config/views.js file, and you'll likely need to run npm install --save consolidate for your project. See the "Views" section below for more details.

, you’ll need to configure it yourself in the file, and you'll likely need to run for your project. See the "Views" section below for more details. If your api or config folders and subfolders contain any non-source files, they’ll need to be moved. The exception is for Markdown (.md) and text (.txt) files, which will continue to be ignored. Sails will attempt to read all other files in those folders as code, allowing for more flexibility in choosing between Javascript dialects (see the notes about CoffeeScript and TypeScript above).

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.

If your app uses blueprint routes , be aware that a couple of implicit "shadow" routes have had their HTTP method (aka verb) changed: the RESTful blueprint route address for add has changed from POST to PUT . the RESTful blueprint route address for update has changed from PUT to PATCH .

, be aware that a couple of implicit "shadow" routes have had their HTTP method (aka verb) changed: If your app relies on the default socket notifications from blueprint actions , be aware that there have been some performance-related upgrades that change the structure of these messages somewhat: Sails no longer publishes separate addedTo notifications, one for each new member of a collection. Those individual notifications are now rolled up into a single notification, and the new message contains an array of ids ( addedIds ) instead of just one. Sails no longer publishes separate removedFrom notifications, one for each former member of a collection. Sails now rolls those up into a single notification, and the new message now contains an array of ids ( removedIds ) instead of just one.

, be aware that there have been some performance-related upgrades that change the structure of these messages somewhat:

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.

Several properties and methods on req now work a little differently: req.accepted has been replaced with req.accepts() req.acceptedLanguages and req.acceptsLanguage() have been replaced with req.acceptsLanguages() req.acceptedCharsets and req.acceptsCharset() have been replaced with req.acceptsCharsets()

Several req.options properties related to blueprints are no longer supported. Instead, the new parseBlueprintOptions method can be used to give you complete control over blueprint behavior. See the blueprints configuration reference for more information.

Instead, the new method can be used to give you complete control over blueprint behavior. See the blueprints configuration reference for more information. The defaultLimit and populate blueprint configuration options are no longer supported. Instead, the new parseBlueprintOptions method can be used to give you complete control over blueprint behavior. See the blueprints configuration reference for more information.

Instead, the new method can be used to give you complete control over blueprint behavior. See the blueprints configuration reference for more information. The .findOne() query method no longer supports sort and limit modifiers, and will throw an error if the given criteria match more than one record . If you want to find a single record using anything besides a unique attribute (like the primary key) as criteria, use .find(<criteria>).limit(1) instead (keeping in mind that this will return an array of one item).

. If you want to find a single record using anything besides a attribute (like the primary key) as criteria, use instead (keeping in mind that this will return an array of one item). autoPk , autoCreatedAt and autoUpdatedAt are no longer supported as top-level model properties. See the migration guide section on model config changes for more information.

are no longer supported as top-level model properties. See the migration guide section on model config changes for more information. Dynamic finders (such as User.findById() ) are no longer added to your models automatically. You can implement these yourself as custom model methods.

(such as ) are no longer added to your models automatically. You can implement these yourself as custom model methods. Model Instance Methods are no longer supported. This allows records returned from find queries to be plain JavaScript objects instead of model record instances.

are no longer supported. This allows records returned from find queries to be plain JavaScript objects instead of model record instances. Custom .toJSON() instance methods are no longer supported. Instead, add a customToJSON method to the model class (outside of the attributes dictionary). See the model settings documentation for more information.

instance methods are no longer supported. Instead, add a method to the model class (outside of the dictionary). See the model settings documentation for more information. The .toObject() instance method is no longer added to every record. When implementing customToJSON for a model, be sure to clone the record using _.omit() , _.pick() or _.clone() .

is no longer added to every record. When implementing for a model, be sure to clone the record using , or . autoUpdatedAt timestamps can now be manually updated in calls to .update() (previously, the passed-in attribute value would be ignored). The previous behavior faciliated the use of .save() , which is no longer supported. Now, you can update the updatedAt if you need to (but generally you should let Sails do this for you!)

in calls to (previously, the passed-in attribute value would be ignored). The previous behavior faciliated the use of , which is no longer supported. Now, you can update the if you need to (but generally you should let Sails do this for you!) beforeValidate and afterValidate lifecycle callbacks no longer exist . Use one of the many other lifecycle callbacks to tap into the query.

. Use one of the many other lifecycle callbacks to tap into the query. afterDestroy lifecycle callback now receives a single record . It has been normalized to work the same way as the afterUpdate callback and call the function once for each record that has been destroyed rather than once with all the destroyed records.

. It has been normalized to work the same way as the callback and call the function once for each record that has been destroyed rather than once with all the destroyed records. Many resourceful PubSub methods have changed (see the PubSub section below for the full list). If your app only uses the automatic RPS functionality provided by blueprints (and doesn’t call RPS methods directly), no updates are required.

(see the PubSub section below for the full list). If your app only uses the automatic RPS functionality provided by blueprints (and doesn’t call RPS methods directly), no updates are required. The .find() model method no longer automatically coerces constraints that are provided for unrecognized attributes . For example, if you execute Purchase.find({ amount: '12' }) , e.g. via blueprints (http://localhost:1337/purchase?amount=12), and there is no such "amount" attribute, then even if the database contains a record with the numeric equivalent ( 12 ), it will not be matched. (This is only relevant when using MongoDB and sails-disk.) If you are running into problems because of this, either define the attribute as a number or (if you're using blueprints) use an explicit where clause instead (e.g. http://localhost.com:1337/purchase?where={"amount":12} ).

. For example, if you execute , e.g. via blueprints (http://localhost:1337/purchase?amount=12), and there is no such "amount" attribute, then even if the database contains a record with the numeric equivalent ( ), it will not be matched. (This is only relevant when using MongoDB and sails-disk.) If you are running into problems because of this, either define the attribute as a number or (if you're using blueprints) use an explicit clause instead (e.g. ). Custom blueprints and the associated blueprint route syntax have been removed . This functionality can be replicated using custom actions, helpers, and routes. See the "Replacing custom blueprints" section below for more information.

. This functionality can be replicated using custom actions, helpers, and routes. See the "Replacing custom blueprints" section below for more information. Blueprint action shadow routes no longer include /:id? at the end -- that is, if you have a UserController.js with a tickle action, you will no longer get a /user/tickle/:id? route (instead, it will be just /user/tickle ). Apps relying on those routes should add them manually to their config/routes.js file.

at the end -- that is, if you have a with a action, you will no longer get a route (instead, it will be just ). Apps relying on those routes should add them manually to their file. sails.getBaseUrl , deprecated in v0.12.x, has been removed. See the v0.12 docs for getBaseUrl for more information on why it was removed and how you should replace it.

, deprecated in v0.12.x, has been removed. See the v0.12 docs for for more information on why it was removed and how you should replace it. req.params.all() , deprecated in v0.12.x, has been removed. Use req.allParams() instead.

, deprecated in v0.12.x, has been removed. Use instead. sails.config.dontFlattenConfig , deprecated in v0.12.x, has been removed. See the original notes about dontFlattenConfig for details.

, deprecated in v0.12.x, has been removed. See the original notes about for details. The order of precedence for req.param() and req.allParams() has changed. It is now consistently path > body > query (that is, url path params override request body params, which override query string params).

It is now consistently path > body > query (that is, url path params override request body params, which override query string params). req.validate() has been removed. Use actions2 instead.

has been removed. Use instead. The default res.created() response has been removed. If you’re calling res.created() directly in your app, and you don't have an api/responses/created.js file, you’ll need to create one. On a related note, the Blueprint create action will now return a 200 status code upon success, instead of 201.

If you’re calling directly in your app, and you don't have an file, you’ll need to create one. The default notFound and serverError responses no longer accept a pathToView argument. They will only attempt to serve a 404 or 500 view. If you need to be able to call these responses with different views, you can customize the responses by adding api/responses/notFound.js or api/responses/serverError.js files to your app.

They will only attempt to serve a or view. If you need to be able to call these responses with different views, you can customize the responses by adding or files to your app. The default badRequest or forbidden responses no longer display views . If you don’t already have the api/responses/badRequest.js and api/responses/forbidden.js files, you’ll need add them yourself and write custom code if you want them to display view files.

. If you don’t already have the and files, you’ll need add them yourself and write custom code if you want them to display view files. The connect-flash middleware has been removed (so req.flash() will no longer be available by default). If you wish to continue using req.flash() , run npm install --save connect-flash in your app folder and add the middleware manually.

(so will no longer be available by default). If you wish to continue using , run in your app folder and add the middleware manually. The POST /:model/:id blueprint RESTful route has been removed. If your app is relying on this route, you’ll need to add it manually to config/routes.js and bind it to a custom action.

If your app is relying on this route, you’ll need to add it manually to and bind it to a custom action. The handleBodyParserError middleware has been removed ; in its place, the Skipper body parser now has its own onBodyParserError method. If you have customized the middleware order, you’ll need to remove handleBodyParserError from the array. If you've overridden handleBodyParserError , you’ll need to instead override bodyParser with your own customized version of Skipper, including your error-handling logic in the onBodyParserError option.

; in its place, the Skipper body parser now has its own method. The methodOverride middleware has been removed. If your app utilizes this middleware: npm install --save method-override Make sure your sails.config.http.middleware.order array (in config/http.js ) includes methodOverride somewhere before router Add methodOverride: require('method-override')() to sails.config.http.middleware .

If your app utilizes this middleware: The router middleware is no longer overrideable. Instead, the Express 4 router is used for routing both external and internal (aka “virtual”) requests. It’s still important to have a router entry in sails.config.http.middleware.order to delimit which middleware should be added before and after the router.

Instead, the Express 4 router is used for routing both external and internal (aka “virtual”) requests. It’s still important to have a entry in to delimit which middleware should be added before and after the router. The query modifiers lessThan , lessThanOrEqual , greaterThan , and greaterThanOrEqual have been removed . Use the shorthand versions instead ( < , <= , > , >= ).

. Use the shorthand versions instead ( , , , ). The find one and find blueprint actions now accept a populate=false rather than populate= to specify that no attributes should be populated.

now accept a rather than to specify that no attributes should be populated. The add and remove blueprint actions now require that the primary key of the child record to add or remove be supplied as part of the URL, rather than allowing it to be passed on the query string or in the body.

now require that the primary key of the child record to add or remove be supplied as part of the URL, rather than allowing it to be passed on the query string or in the body. The destroy blueprint action now requires that the primary key of the record to destroy be supplied as part of the URL, rather than allowing it to be passed on the query string or in the body.

now requires that the primary key of the record to destroy be supplied as part of the URL, rather than allowing it to be passed on the query string or in the body. The sails.config.session.routesDisabled setting has changed to sails.config.session.isSessionDisabled() , a function. See the config/session.js docs for more information on configuring isSessionDisabled() .

to , a function. See the docs for more information on configuring . The experimental “switchback-style” usage for Waterline methods is no longer supported . Only function callbacks may be used with Waterline model methods.

. Only function callbacks may be used with Waterline model methods. The experimental create auto-migration scheme is no longer supported . It is highly recommended that you use a migration tool such as Knex to handle migrations of your production database.

. It is highly recommended that you use a migration tool such as Knex to handle migrations of your production database. The experimental forceLoadAdapter datastore setting is no longer supported . Instead, all adapters referenced in config/datastores.js (formerly config/connections.js ) are automatically loaded whenever Sails lifts.

. Instead, all adapters referenced in (formerly ) are automatically loaded whenever Sails lifts. The experimental usage route option has been removed. It is recommended that you perform any route parameter validation in your controller code.

It is recommended that you perform any route parameter validation in your controller code. The experimental “associated-item” blueprint shadow routes have been removed. These were routes like GET /user/1/pets/2 , whose functionality can be replicated by simply using the much-clearer route GET /pets/2 .

These were routes like , whose functionality can be replicated by simply using the much-clearer route . The experimental .validate() method in model classes (e.g. User.validate() ) is now fully supported, but its usage has changed. See the .validate() docs for more information.

(e.g. ) is now fully supported, but its usage has changed. See the docs for more information. The ordering of attributes in the internal representation of model classes has changed (association attributes are now sorted at the bottom). This has the effect of causing tables created using migrate: 'alter' to have their columns in a different order than in previous versions of Waterline, so be aware of this if column ordering is important in your application. As a reminder, auto-migrations are intended to help you design your schema as you build your app. They are not guaranteed to be consistent regarding any details of your physical database columns besides setting the column name, type (including character set / encoding if specified) and uniqueness.

in the internal representation of model classes has changed (association attributes are now sorted at the bottom). This has the effect of causing tables created using to have their columns in a different order than in previous versions of Waterline, so be aware of this if column ordering is important in your application. As a reminder, auto-migrations are intended to help you design your schema as you build your app. They are not guaranteed to be consistent regarding any details of your physical database columns besides setting the column name, type (including character set / encoding if specified) and uniqueness. Using _config to link a controller to a model will no longer work. This was never a supported feature, but it was used in some projects to change the URLs that were mapped to the blueprint actions for a model. Please use restPrefix instead.

Changes to database configuration

The sails.config.connections setting has been deprecated in favor of sails.config.datastores . If you lift an app that still has sails.config.connections configured, you’ll get a warning which you can avoid by simply changing module.exports.connections in config/connections.js to module.exports.datastores . For your own sanity, it’s recommended that you also change the filename to config/datastores.js .

setting has been deprecated in favor of . If you lift an app that still has configured, you’ll get a warning which you can avoid by simply changing in to . For your own sanity, it’s recommended that you also change the filename to . The sails.config.models.connection setting has been deprecated in favor of sails.config.models.datastore . As above, changing the name of the property in config/models.js should be sufficient to turn off any warnings.

setting has been deprecated in favor of . As above, changing the name of the property in should be sufficient to turn off any warnings. Every app now has a default datastore (appropriately named default ) that is configured to use a built-in version of the sails-disk adapter. In Sails 1.0, the default value of sails.config.models.datastore is default (rather than localDiskDb ). The recommended approach to setting the default datastore for your models is to simply to add the desired configuration under the default key in config/datastores.js , and leave the datastore key in config/models.js undefined, rather than the previous approach of setting datastore to (for example) myPostgresqlDb and then adding a myPostgresqlDb key to config/datastores.js . This makes it a lot easier to change the datastore used by different environments (for instance, by changing the configuration of the default datastore in config/env/production.js ).

) that is configured to use a built-in version of the adapter. In Sails 1.0, the default value of is (rather than ). The recommended approach to setting the default datastore for your models is to simply to add the desired configuration under the key in , and leave the key in undefined, rather than the previous approach of setting to (for example) and then adding a key to . This makes it a lot easier to change the datastore used by different environments (for instance, by changing the configuration of the datastore in ). All datastores that are configured in an app will be loaded at runtime (rather than only loading datastores that were being used by at least one model). This has the benefit of allowing the use of a datastore outside the context of an individual model, but it does mean that if you don’t want to connect to a certain database when Sails lifts, you should comment out that datastore connection config!

The .create() , .update() and .add() model methods no longer support creating a new “child” record to link immediately to a new or existing parent. For example, given a User model with a singular association to an Animal model through an attribute called pet , it is not possible to set pet to a dictionary representing values for a brand new Animal (aka a “nested create”). Instead, create the new Animal first and use its primary key to set pet when creating the new User .

, and model methods no longer support creating a new “child” record to link immediately to a new or existing parent. For example, given a model with a singular association to an model through an attribute called , it is not possible to set to a dictionary representing values for a brand new (aka a “nested create”). Instead, create the new first and use its primary key to set when creating the new . Similarly, the create, update and add blueprint actions no longer support nested creates.

The .update() model method and its associated blueprint action no longer support replacing an entire plural association. If a record is linked to one or more other records via a “one-to-many” or “many-to-many” association and you wish to link it to an entirely different set of records, use the .replaceCollection() model method or the replace blueprint action.

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.

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:

For type: 'string' , these timestamps are stored in the same way as they were in Sails 0.12: as timezone-agnostic ISO 8601 JSON timestamp strings (e.g. '2017-12-30T12:51:10Z' ). So if any of your front-end code is relying on the timestamps as strings it's important to set this to string .

, these timestamps are stored in the same way as they were in Sails 0.12: as timezone-agnostic ISO 8601 JSON timestamp strings (e.g. ). So if any of your front-end code is relying on the timestamps as strings it's important to set this to . For type: 'number' , these timestamps are stored as JS timestamps (the number of milliseconds since Jan 1, 1970 at midnight UTC).

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.

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

For performance reasons, as of Sails v1.0 / Waterline 0.13, criteria passed into Waterline's model methods will now be mutated in-place in most situations (whereas in Sails/Waterline v0.12, this was not necessarily the case).

Aggregation clauses ( sum , average , min , max , and groupBy ) are no longer supported in criteria. Instead, see new model methods .sum() and .avg().

, , , , and ) are no longer supported in criteria. Instead, see new model methods .sum() and .avg(). Changes to limit and skip: limit: 0 no longer does the same thing as limit: undefined . Instead of matching ∞ results, it now matches 0 results. Avoid specifying a limit of < 0. It is still ignored, and acts like limit: undefined , but it now logs a deprecation warning to the console. skip: -20 no longer does the same thing as skip: undefined . Instead of skipping zero results, it now refuses to run with an error. Limit must be < Number.MAX_SAFE_INTEGER (...with one exception: for compatibility/convenience, Infinity is tolerated and normalized to Number.MAX_SAFE_INTEGER automatically.) Skip must be < Number.MAX_SAFE_INTEGER



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:

Change module.exports.cors to module.exports.security.cors in config/cors.js

to in Change CORS config settings names to match the newly documented names in Reference > Configuration > sails.config.security

Change module.exports.csrf to module.exports.security.csrf in config/csrf.js . This value is now simply true or false ; no other CSRF options are supported (see below).

to in . This value is now simply or ; no other CSRF options are supported (see below). sails.config.csrf.routesDisabled is no longer supported. Instead, add csrf: false to any route in config/routes.js that you wish to be unprotected by CSRF, for example:

'POST /some-thing': { action: 'do-a-thing', csrf: false },

sails.config.csrf.origin is no longer supported. Instead, you can add any custom CORS settings directly to your CSRF token route configuration, for example:

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

sails.config.csrf.grantTokenViaAjax is no longer supported. This setting was used to turn the CSRF token-granting route on or off. In Sails 1.0, you add that route manually in your config/routes.js file (see above). If you don’t want to grant CSRF tokens via AJAX, just leave that route out of config/routes.js .

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

Removed deprecated backwardsCompatibilityFor0.9SocketClients setting.

setting. Removed deprecated .subscribers() method.

method. Removed deprecated "firehose" functionality.

Removed support for 0.9.x socket client API.

The following resourceful pubsub methods have also been removed: .publishAdd() .publishCreate() .publishDestroy() .publishRemove() .publishUpdate() .watch() .unwatch() .message()



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:

The 404 , 500 and startRequestTimer middleware are now built-in to every Sails app, and have been removed from the sails.config.http.middleware.order array. If your app has an overridden 404 or 500 handler, you should instead override api/responses/notFound.js and api/responses/serverError.js respectively.

, and middleware are now built-in to every Sails app, and have been removed from the array. If your app has an overridden or handler, you should instead override and respectively. Session middleware that was designed specifically for Express 3 (e.g. very old versions of connect-redis or connect-mongo ) will no longer work, so you’ll need to upgrade to more recent versions.

or ) will no longer work, so you’ll need to upgrade to more recent versions. The sails.config.http.customMiddleware feature is deprecated in Sails 1.0. It will still work for now, but may be removed in a later release. Instead of using customMiddleware to modify the Express app directly, use regular ( req , res , next ) middleware instead. For instance, you can replace something like:

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

.jsonx() is deprecated. If you have files in api/responses that you haven't customized at all, you can just delete them and let the Sails default responses work their magic. If you have files in api/responses that you’d like to keep, replace any occurences of res.jsonx() in those files with res.json() .

is deprecated. If you have files in that you haven't customized at all, you can just delete them and let the Sails default responses work their magic. If you have files in that you’d like to keep, replace any occurences of in those files with . res.negotiate() is deprecated. Use res.serverError() , res.badRequest() , or a custom response instead.

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:

The default transports setting is simply ['websocket'] . In the majority of production deployments, restricting your app to the websocket transport (rather than using ['polling', 'websocket'] ) avoids problems with sessions (see the pre-1.0 scaling guide notes for details). If you’re using the sails.io.js websocket client, the easiest way to make your app compatible with the new websocket settings is to install the new sails.io.js version with sails generate sails.io.js . The latest version of that package also defaults to the “websocket-only” transport strategy. If you’ve customized the transports setting in your front-end code and config/sockets.js file, then you'll just need to continue to ensure that the values in both places match.

setting is simply . In the majority of production deployments, restricting your app to the transport (rather than using ) avoids problems with sessions (see the pre-1.0 scaling guide notes for details). If you’re using the websocket client, the easiest way to make your app compatible with the new websocket settings is to install the new version with . The latest version of that package also defaults to the “websocket-only” transport strategy. If you’ve customized the setting in your front-end code and file, then you'll just need to continue to ensure that the values in both places match. The latest sails-hook-sockets hook uses a newer version of Socket.io. See the Socket.io changelog for a full update, but keep in mind that socket IDs no longer have /# prepended to them by default.

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.