3.0.0 Release Candidate
Pre-releaseThis is the first Release Candidate release of the upcoming major version 3.0.
List of notable changes made between 2.x and 3.0 in this repository follows below.
Invocation for remote method has been changed.
We are deprecating the SharedClass.prototype.find
and
SharedClass.prototype.disableMethod
for looking up and disabling remote
methods in 3.0. They will be replaced by SharedClass.prototype.findMethodByName
and SharedClass.prototype.disableMethodByName
where you can pass in a static
method or a prototype method name. These new methods will accept a string in the
form of "name" for a static method and "prototype.name" for a prototype method.
To find a static remote method:
findMethodByName('create')
To find a prototype remote method:
findMethodByName('prototype.updateAttributes')
To disable a static remote method
disableMethodByName('create')
To disable a prototype remote method:
disableMethodByName('prototype.updateAttributes')
New error-handler for rest-adapter
The REST adapter (typically created via loopback.rest()
) uses a new error
handler implementation that provides more secure configuration out of the box.
The error.status
has been removed and will be replaced by error.statusCode
,
statusCode
is more descriptive and avoids ambiguity, users relying on status
will need to change implementation accordingly.
To replicate old behavior user can specify config.local.js
as follow:
module.exports = {
remoting : {
errorHandler: {
handler : function(err, req, res, defaultHandler) {
err.status = err.statusCode;
defaultHandler();
}
}
}
}
The environment setting NODE_ENV='production'
is no longer supported,
production vs. debug mode is controlled exclusively by configuration.
Production environment is assumed by default, the insecure debug mode
must be explicitely turned on.
You can learn more about the rationale behind the new handler in
this comment
User can specific options in their applications in config.json
as follow:
{
"restApiRoot": "/api",
"host": "0.0.0.0",
"port": 3000,
"remoting": {
"errorHandler": {
"debug": false,
"log": false
}
}
}
Production mode
Stack trace is never returned in HTTP responses.
Bad Request errors (4xx) provide the following properties copied from the
error object: name
, message
, statusCode
and details
.
All other errors (including non-Error values like strings or arrays) provide
only basic information: statusCode
and message
set to status name from HTTP
specification.
Debug mode
When in debug mode, HTTP responses include all properties provided by the error.
For errors that are not an object, their string value is returned in
message
field.
When a method fails with an array of errors (e.g. bulk create), HTTP the response
contains still a single wrapper error with details
set to the original array
of errors.
An example of an error response when an array of errors was raised:
{
error: {
statusCode: 500,
name: 'ArrayOfErrors',
message: 'Failed with multiple errors, see `details` for more information.',
details: [
{ name: 'Error1', message: 'expected error', statusCode: 500, stack: '<stacktrace>' },
{ name: 'Error2', message: 'expected error2', statusCode: 500, stack: '<stacktrace>'}
]
}
}
Please see Related code change here, and new strong-error-handler
here.
Type converters replace Dynamic
API
The Dynamic
component was removed in favour of type converters and a
registry.
The following APIs were removed:
RemoteObjects.convert(name, fn)
remoteObjectsInstance.convert(name, fn)
RemoteObjects.defineType(name, fn)
remoteObjectsInstance.defineType(name, fn)
Two new APIs are added as a replacement:
remoteObjectsInstance.defineType(name, converter)
remoteObjectsInstance.defineObjectType(name, factoryFn)
See the API docs and
pull request #343
for more details.
Conversion and coercion of input arguments
We have significantly reworked conversion and coercion of input arguments
when using the default REST adapter. The general approach is to make both
conversion and coercion more strict. When we are not sure how to treat
an input value, we rather return HTTP error 400 Bad Request
than coerce
the value incorrectly.
Most notable breaking changes:
null
value is accepted only for "object", "array" and "any"- Empty string is coerced to undefined to support ES6 default arguments
- JSON requests providing scalar values for array-typed argument are
rejected - Empty value is not converted to an empty array
- Array values containing items of wrong type are rejected. For
example, an array containing a string value is rejected when
the input argument expects an array of numbers. - Array items from JSON requests are not coerced. For example,
[true]
is no longer coerced to[1]
for number arrays,
and the request is subsequently rejected. - Deep members of object arguments are no longer coerced. For example,
a query like?arg[count]=42
produces{ count: '42' }
now. - "any" coercion preserves too large numbers as a string, to prevent
losing precision. - Boolean types accepts only four string values:
'true', 'false', '0' and '1'
Values are case-insensitive, i.e. 'TRUE' and 'FaLsE' work too. - Date type detects "Invalid Date" and rejects such requests.
- When converting a value coming from a string source like querystring
to a date, and the value is a timestamp (i.e. an integer), we treat
the value as a number now. That way the value "0" always produces
"1970-01-01T00:00:00.000Z" instead of some date around 1999/2000/2001
depending on server timezone. - Array values are not allowed for arguments of type object.
Hopefully this change should leave most LoopBack applications (and clients)
unaffected. If your start seeing unusual amount of 400 error responses after
upgrading to LoopBack 3.x, then please check the client code and ensure it
correctly encodes request parameters.
See the following pull requests for more details:
Serialization of Date values in responses
The format of date values has been changed from the output of .toString()
,
which produces values in local timezone, to the output of .toJSON()
, which
produces values in GMT.
For example, let's take new Date(0)
returned for dateArgument
.
In strong-remoting 2.x, this value is converted to the following response
when running on a computer in the Central-European timezone:
{
"dateArgument": {
"$type": "date",
"$data": "Thu Jan 01 1970 01:00:00 GMT+0100 (CET)"
}
}
In strong-remoting 3.x, the same value is converted to the following response
regardless of the server timezone settings:
{
"dateArgument": {
"$type": "date",
"$data": "1970-01-01T00:00:00.000Z"
}
}
See pull request #344 for more details.
CORS is no longer enabled
There are two types of applications that are commonly built using LoopBack,
and each type has different requirements when it comes to CORS.
- Public facing API servers serving a variety of clients.
CORS should be enabled to allow 3rd party sites to access the API server
living on a different domain. - API+SPA sites where both API and the SPA client live on the same domain
and the API should be locked down to serve only its own browser clients,
i.e. CORS must be disabled.
By enabling CORS by default, we were making API+SPA sites vulnerable by default.
In strong-remoting 3.x, we removed the built-in CORS middleware and pushed
the responsibility for enabling and configuring CORS back to application
developers.
Note that this does not affect LoopBack applications scaffolded by a recent
version of slc loopback
or apic loopback
, as these applications don't rely
on the built-in CORS middleware and configure CORS explicitly
in server/middleware.json
.
If your application relies on the built-in CORS middleware that used to be
provided by strong-remoting/loopback, then please make the following changes
to upgrade your application:
npm install --save cors
2a) If you are using loopback-boot, then add the following entry to your
server/middleware.json
file:
{
// ...
"initial": {
// ...
"cors": {
"params": {
"origin": true,
"credentials": true,
"maxAge": 86400
}
},
// ...
},
// ...
}
2b) If you are not using loopback-boot, then configure the cors
middleware
in your server script:
// ...
var cors = require('cors');
app.use(cors({ origin: true, credentials: true, maxAge: 86400 }));
// ...
It's important to add the cors
middleware early, before any request-parsing
middleware.
See pull request #352 and
Security considerations
for more details.