This one is causing me a lot of pain.
After a lot of debugging it looks like Loopback is automatically assuming an ID value on the URI (e.g. /api/myModel/:id) is a Number.
So something like this:
curl -X GET --header "Accept: application/json" "http://192.168.99.100:3000/api/profiles/5828128208445124611"
Does not work because Loopback is automatically converting the BIGINT value to Number, which rounds 5828128208445124611 to 5828128208445125000.
So the above GET request example, results in an error:
{
"error": {
"name": "Error",
"status": 404,
"message": "Unknown \"Profile\" id \"4828128208445124611\".",
"statusCode": 404,
"code": "MODEL_NOT_FOUND",
"stack": "Error: Unknown \"Profile\" id \"4828128208445124611\".\n at Function.convertNullToNotFoundError (/usr/src/app/node_modules/loopback/lib/persisted-model.js:81:17)\n at invokeRestAfter (/usr/src/app/node_modules/loopback/node_modules/strong-remoting/lib/rest-adapter.js:453:25)\n at /usr/src/app/node_modules/loopback/node_modules/async/lib/async.js:607:21\n at /usr/src/app/node_modules/loopback/node_modules/async/lib/async.js:246:17\n at iterate (/usr/src/app/node_modules/loopback/node_modules/async/lib/async.js:146:13)\n at /usr/src/app/node_modules/loopback/node_modules/async/lib/async.js:157:25\n at /usr/src/app/node_modules/loopback/node_modules/async/lib/async.js:248:21\n at /usr/src/app/node_modules/loopback/node_modules/async/lib/async.js:612:34\n at interceptInvocationErrors (/usr/src/app/node_modules/loopback/node_modules/strong-remoting/lib/remote-objects.js:681:22)\n at /usr/src/app/node_modules/loopback/node_modules/async/lib/async.js:154:25\n at /usr/src/app/node_modules/loopback/node_modules/async/lib/async.js:154:25\n at /usr/src/app/node_modules/loopback/node_modules/async/lib/async.js:154:25\n at execStack (/usr/src/app/node_modules/loopback/node_modules/strong-remoting/lib/remote-objects.js:488:7)\n at RemoteObjects.execHooks (/usr/src/app/node_modules/loopback/node_modules/strong-remoting/lib/remote-objects.js:492:10)\n at phaseAfterInvoke (/usr/src/app/node_modules/loopback/node_modules/strong-remoting/lib/remote-objects.js:652:10)\n at runHandler (/usr/src/app/node_modules/loopback/node_modules/loopback-phase/lib/phase.js:135:5)"
}
}
Enabling debugging output in Loopback shows:
Tue, 17 May 2016 19:52:05 GMT loopback:connector:postgresql SQL: SELECT "user_id","first_name","last_name","email","phone","created_at","updated_at","created_by","updated_by" FROM "user_profile"."profile" WHERE "user_id"=$1 ORDER BY "user_id" LIMIT 1
Parameters: ["4828128208445125000"]
So clearly the value 4828128208445124611 gets rounded into 4828128208445125000.
I verified that this happens right at the beginning, using an operations hook:
Profile.observe('access', function logQuery(ctx, next) {
console.log('Context', ctx);
next();
});
The output shows the ID is already truncated:
query:
{ where: { userId: '4828128208445125000' },
limit: 1,
offset: 0,
skip: 0 },
Is this a known issue? I couldn't find documentation or bug reports etc. on this.
Btw, adding my own remoteMethod where I have control over the Input Argument (i.e. forcing it to be a string) and then calling findById programmatically works correctly:
Profile.get = function(id, cb) {
console.log('ID:', id);
Profile.findById(id).then(function(profile) {
console.log('FindById: profile', profile);
cb(null, profile);
});
};
Profile.remoteMethod(
'get',
{
accepts: [
{arg: 'id', type: 'string', required: true}
],
http: {path: '/:id/get', verb: 'get'},
returns: {arg: 'profile', type: 'object'}
}
);
You can see above I simply am copying the default findById REST endpoint, only difference is I add get to the end of the URI /api/profiles/:id/get. The logic just defers to Profile.findById().
Calling it:
curl -X GET --header "Accept: application/json" "http://192.168.99.100:3000/api/profiles/4828128208445124611/get"
Works with no issues.
Looks like this is the same issue as https://github.com/strongloop/loopback/issues/1870 which is supposed to be fixed by https://github.com/strongloop/loopback/pull/1221.
@raymondfeng are there still plans to land #1221? Maybe someone else should take it over if not.
@bajtos, is this related to coercion clean-up in strong-remoting? Do you have any PRs that fix the issue?
@richardpringle I think it may be related to coercion clean-up, thank you for pointing it out!
This problem has been fixed by #343 in LoopBack 3.0 (alpha). When converting "any"-typed argument from a string-source like a query string, we preserve string values that are too large to store in an integer (greater than MAX_SAFE_INTEGER).
@bajtos Any chance this can this be backported to 2.x? We've been bitten by this a couple of times.
@lukewendling @justinlueders
I am afraid there is no easy way how to backport the fix to 2.x, as the coercion code was completely rewritten in 3.0.
I think this is the place where to fix the problem in 2.x, if you feel like contributing the fix yourself:
https://github.com/strongloop/strong-remoting/blob/c88d61c3334bcc64e94727bf482904900f134590/lib/http-context.js#L298
I am using version loopback ver 3.22.0 with loopback-connector-mssql ver 3.3.0.
when setting a model properties as the following:
"objectId": {
"type": "Number",
"id": true,
"generated": true,
"required": false,
"length": null,
"precision": null,
"scale": null,
"mssql": {
"columnName": "objectId",
"dataType": "bigint",
"dataLength": null,
"dataPrecision": null,
"dataScale": null,
"nullable": "NO"
}
(notice the "id":true and "dataType": "bigint")
LB round the BIGINT params.
Changing the id property to false fixes the issue, but loopback does not allow models with no id when connecting a DB, I used a very unprofessional work around at the moment)
Is this issue still marked as an open bug?