I want to create a model in loopback with very complex logic, impossible to map to any datasource. SO I would like to somehow only generate the CRUD methods skeletons in JS and be able to simply override them, as explained here:
extend the CRUD method in LoopBack
From the outside it should be accessible as any REST API, with all the CRUDs and other methods, typical in loopback.
I would also apply ACLs, authorization and all the stuff to it, just as normal.
How should I proceed?
Is this case somewhere formally documented?
Are the CRUD methods officially documented, so I can safely override them?
You can create it with the lb model command. Be sure to select:
Datasource: (no datasource)
Model: Model
Expose: Yes
Common/server: Common
This will create the files inside common/models. You can do this manually too. A datasource-less model is essentially composed of these file contents:
test.json
{
"name": "test",
"base": "Model",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {},
"validations": [],
"relations": {},
"acls": [],
"methods": {}
}
test.js
'use strict';
module.exports = function(Test) {
Test.greet = function(msg, cb) {
cb(null, 'Greetings... ' + msg);
}
Test.remoteMethod('greet', {
accepts: { arg: 'msg', type: 'string' },
returns: { arg: 'greeting', type: 'string' }
});
};
This will create a route called /test, with a function named "greet".
The loopback node API is documented there.
Just override the methods like in the link you provided. You will need to match the node API of the original method in your overriden method, but apart from that no restrictions. ACLs are decoupled from that so nothing to worry on this side.
However, I don't know how you plan to write a stateless loopback application without using a datasource, since this is where the state is stored. If your loopback application is not stateless, remember that it will not scale (cannot start multiple instances in a cluster), and will do nasty things when it will crash. Can't you just split your problem / simplify it ?
Related
I currently have a response like so from my API:
{
"data": [{
"attributes": {
"customer_name": "The Name",
},
"id": 1,
"relationships": {
"tasks": {
"data": [{
"attributes": {
"name": "The task",
},
]
}
},
"type": "customer"
}
]
}
And Ember loads the model correctly. However, when I try and consume the computed property the relationship creates, Ember initiates an OPTIONS and subsequent GET request for each item in the relationship.
I haven't worked with these kinds of relationships before so this may be the expected behaviour but I'm a bit confused.
If it is going to do a request for each one, why bother sending the items as part of the original model via a relationship at all? Why not just do a normal model retrieval which would probably be much quicker.
It seems really wasteful so I suspect I'm not handling or understanding something correctly, I'd appreciate any insight.
This is expected behavior for what you are doing (accessing a hasMany relationship). In your example, the tasks do not exist in Ember Data's store so when trying to access them Ember Data is smart enough to fetch them for you from your API. In doing so, it sends out the OPTIONS and GET requests to retrieve them. The OPTIONS request is simply a preflight request.
If you wanted to work with this particular model and its tasks (or other hasMany relationship models) without making so many network requests, you could fetch both simultaneously by requesting them with an include assuming you're using an adapter that allows it (i.e., supports the JSON API spec) and your API does too:
// for example, fetching a post with its comments (hasMany relationship)
this.store.findRecord('post', params.post_id, { include: 'comments' } );
Doing so should return the model and its tasks in one network request. For further reading, I'd checkout Ember's guide on Relationships.
Loopback uses sequential number for model ID. Can I use my own ID generator on server side? How do I go about doing that?
It is possible to specify Loopback generators (guid, uuid, ...) as a default function for id properties in your model definition file.
example with guid:
{
"name": "ModelName",
"base": "PersistedModel",
"idInjection": false,
"properties": {
"id": {
"type": "string",
"id": true,
"defaultFn": "guid"
}
},
"validations": [],
"relations": {},
"acls": [],
"methods": {}
}
As far as I know, you can't specify there your own default function yet. See related github issue.
If you want more advanced behavior (e.g. your own generator), you can create models/model-name.js file and extend a constructor of your model.
Yes, you would need to do a few things:
Set "idInjection": false in the corresponding model.json to turn off automatic id injection
Add the property you want to your model, then set it to be an id either by setting "id": true on the property in the model.json, or selecting the id radial next to the prop in the composer
Generate and inject the id, probably with an operation hook on before save (https://docs.strongloop.com/display/public/LB/Operation+hooks) or maybe a mixin (https://docs.strongloop.com/display/public/LB/Defining+mixins)
If you use Loopback 4 then this is the setting for generating UUID in prime key.
Inside you Model change this.
#property({
type: 'string',
id: true,
defaultFn: 'uuidv4',
})
id?: string;
This is the way to gen a unique id in your table.
I have created models using slc loopback:model tool. Now I want Loopback to create corresponding MongoDB collections, that is to perform auto-migration.
One of the models is a Client model whose base class is a User model. That means that client/models/client.json is just empty because all its properties (fields) are inherited from User:
{
"name": "Client",
"plural": "Clients",
"base": "User",
"idInjection": true,
"properties": {},
"validations": [],
"relations": {},
"acls": [],
"methods": []
}
So I think to myself that if I make an auto-migration, Loopback finds all User properties and creates Client collection with them. But it doesn't! My Client collection has only _id property.
Here is a my code for auto-migration:
module.exports = function(app) {
app.dataSources.mongodb.automigrate('Client', function(err) {
if (err) throw err;
});
};
My question:
Why Loopback doesn't use User model properties for my Client model? How to auto-migrate so that Loopback will create correct collection?
automigrate is used to migrate model data into tables i.e. Model name as tablename and Model's properties as table columns.
Now as you are using MongoDB, it drops and creates indexes as written in documentation. This is because MongoDB is schemaless.
So, probably you can avoid automigration and insert new documents directly.
I am trying to create an organisations-table component, which will display a list of organisations, and the user for which each organisation belongs. I pass the model for the organisations into the component via an organisations.hbs template, which results in the following response from the server:
{
"organisations": [
{
"id": 0,
"name": "Org0",
"user": 1
},
{
"id": 1,
"name": "Org1",
"user": 2
},
{
"id": 2,
"name": "Org2",
"user": 2
}
]
}
In order to display the username for each user, the component then makes its own call to the server querying against the id for each of the users.
Is this the correct approach? My understanding is that components are supposed to be isolated by design, only aware of the data passed into them, but in this example, the component is sending its own requests to the server for the additional data.
I have created this ember twiddle to hopefully give an idea of the structure of the app (comments welcome!).
Thanks in advance
The component itself has nothing to do with the calls, { async: true } means that the relationship won't be fetched unless it is "needed".
Needed in this case being the organisation.user.username in your component's template.
Keep in mind that model in your case is an array of DS.Model objects that have relationships.
I have User model over relational DB.
Each User can hasMany "users" where "chiefId" is FK.
"relations": {
"users": {
"type": "hasMany",
"model": "User",
"foreignKey": "chiefId"
},
}
I can query related users for each chief-user like this:
GET /users?filter={"include":"users"}
But it returns full user objects.
How should I query only "name" properties of related users?
Also is it possible to count related instances in one request to server?
A late reply but I just ran into this question now. It is possible:
filter: {
include:{
relation: "users",
scope: {
fields:["name"]
}
}
}
As far as I understood this question is about adding a nested filter on an include level, which seems to be not yet supported: https://groups.google.com/forum/#!msg/loopbackjs/T6onsYMJFOI/V4ILc3Obf3MJ
May be it's not the best way to approach this problem, but what you can do is a manual response transformation in .afterRemote('find', ...) hook.
/users?filter[fields][0]=name
See https://github.com/strongloop/loopback-example-relations-basic for more info.