Change loopback default model User to Member - loopbackjs

I just don't want to use the name "User", I want it to be "Member"
//// this is for bypassing stackoverflow qualiy control

You can expand the built-in model User:
https://loopback.io/doc/en/lb3/Extending-built-in-models.html
server/models/Member.json
{
"name": "Member",
"base": "User",
...
}

Related

Loopback - How to access custom method with related model?

First please apologize my english.
I'm trying to access a custom remote method of one of my model with a related model.
I've created 3 models in Loopback which are related this way:
User hasMany Shop & Shop belongTo User
Shop hasMany Credit & Credit belongTo Shop
User.nestRemoting("shops") for User to reach Credit through Shop, User being the main entry point for the API.
Credit can be bought by a User for a Shop to execute some actions which cost Credit.
I've also created a custom remote method for Credit: availableCredit(shopId) which sum the Credit being available (those bought by User for a Shopand those spent by a User for a Shop).
I've registered my custom method in credit.json and it went fine.
"methods": {
"availableCredits": {
"description": Returns the available credits for the shop",
"accessType": "READ",
"shared": true,
"http": {
"path": "/available",
"verb": "get",
"status": 200
},
"accepts": [
{
"arg": "shopId",
"type": "string",
"description": "shop id",
"required": true
}
],
"returns": {
"arg": "credits",
"type": "number"
}
}
}
Everything works BUT my custom method DOESN'T show on API Loopback Explorer under shops or user.
I do see and have access to all the build-in methods like (in user):
GET /users/{id}/shops/{nk}/credits
Or my custom method in credit:
GET /credits/available
but I DON'T have
GET /users/{id}/shops/{nk}/credits/available
nor
GET /shops/{id}/credits/available
Does anyone knows if it's possible and has any idea of how it works ?
Thanks in advance.

Include related model in an hasOne relation

I have two models: Account and Customer both having an email address.
An Account can exist without a Customer and a Customer can exist without an Account.
However, an Account should return the related Customer record if this exists.
I was thinking about doing this by creating a hasOne relation on the Account using the unique identifier available in both records (the email address) as foreignKey.
Unfortunately this is not working.
These are my models:
Account
...
"properties": {
"username": {
"type": [
"string"
]
},
"email": {
"type": "string"
}
},
"validations": [],
"relations": {
"customer": {
"type": "hasOne",
"model": "Customer",
"foreignKey": "email"
}
}
...
Customer
...
"properties": {
"name": {
"type": [
"string"
]
},
"email": {
"type": "string"
}
},
"validations": [],
"relations": {}
...
By calling /api/account?filter={"include": ["customer"]} I don't get any additional information.
I don't understand if the problem is the foreignKey or the relation.
You could use an afterRemote hook to do the marshaling just before returning the requested instance.
However this won't be automatic, i.e. you still need to provide some sort of id to link the two instances together. In your case, if the email is such an id, then you would just search for a Customer instance with the same email as the Account instance.
The advantage is that you don't need to provide any extra filters or anything else to your query.
e.g.
Account.afterRemote('find', function(ctx, modelInstance, next) {
// Here you can check if the Account instance has a Customer instance
// via a regular find or findById, and if you do find the related instance
// you can add the data to ctx.result, which is the object that will be returned.
Customer.find({where:{email: modelInstance.email}}, addCustomerDetails);
function addCustomerDetails(err, linkedCustomer) {
// Add the Customer to the Account instance here
ctx.result.customer = linkedCustomer;
next();
}
});
And of course, you can do the same in the Customer afterRemote hook, but instead searching for the linked Account instance email.
Your models are defined well.
Be sure you have customer instance with existed email in db.
And the correct for of rest api calls is : /api/account?filter[include]=customer
UPDATE
Loopback overwrite the type of email because of the relation. hasOne relation should be setup over id foreign key not any other fields.
So if you want to fix the problem, you need to add below to properties section of account definition :
"id": false,
"email": {
"type": "string",
"id": true
}
The foreignKey field is just an alias for the relation. Having an email property and setting email as foreignKey does not create any sort of link between the two.
Then, it's simply a matter of using the REST API to instanciate the models, setup the relation and fetch the data
create an account
POST api/accounts/
{
"email": "account#bar.com"
}
create a related customer
POST api/accounts/1/foreignKey
{
"email": "customer#bar.com"
}
Fetch the accound and include the related customer
GET api/accounts/1?filter[include]=foreignKey

Can I use non-sequential id for loopback model?

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.

Why auto-migration doesn't create any properties in a Collection? How to make it to create them?

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.

Loopback 2.4: how to query certain fields of related model via REST API

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.