LoopBack Remote Methods and Access to Model Data - node.js

I've been working on this for hours and I'm completely lost, because the loopback documentation is not helpful.
I'm trying to write application logic into a model. The documentation for that is here. Unfortunately, the example doesn't demonstrate anything useful other than passing an external value into the remote method and returning it again. I'd like to understand how to run a query in this context and access model data, but I have searched for hours and not been able to find documentation on even these simple tasks. Maybe I'm just looking in the wrong places. Can anyone help?

Typically, you can accomplish most things you'd want to do such as querying and accessing model data (CRUD operations) through the built-in methods that all models get; see http://docs.strongloop.com/display/LB/Working+with+data. Defining a remote method (custom REST endpoint) for these would be redundant.
You access the standard model CRUD Node APIs (e.g. myModel.create(), myModel.find(), myModel.updateAll() ) in the remote method code if you want to.
You may also find further related examples in https://github.com/strongloop/loopback-example-app-logic
Here's an example using the Getting Started app https://github.com/strongloop/loopback-getting-started app. It defines a remote method that takes a number arg and prints the name of the coffeeshop with that ID to the console:
This code is in common/models/coffeeshop.js:
module.exports = function(CoffeeShop) {
...
// Return Coffee Shop name given an ID.
CoffeeShop.getName = function(shopId, cb) {
CoffeeShop.findById( shopId, function (err, instance) {
response = "Name of coffee shop is " + instance.name;
cb(null, response);
console.log(response);
});
}
...
CoffeeShop.remoteMethod (
'getName',
{
http: {path: '/getname', verb: 'get'},
accepts: {arg: 'id', type: 'number', http: { source: 'query' } },
returns: {arg: 'name', type: 'string'}
}
);
};
You can use the API Explorer to load http://0.0.0.0:3000/explorer/#!/CoffeeShops/getName then enter a number (there are only three coffee shops in the app initially) as the query param and hit "Try It Out!"
Or just GET a URL like http://0.0.0.0:3000/api/CoffeeShops/getid?id=1
Rand

I finally discovered my problem. Object properties must be loaded in a callback of the function calling the CRUD operation. The following syntax worked for me:
module.exports = function (TestModel) {
TestModel.testRemoteMethod = function (id, name, cb) {
TestModel.findOne({where: {id: id}}, function(err, modelInstance) {
//modelInstance has properties here and can be returned to
//the API call using the callback, for example:
cb(null, {"name": modelInstance.name});
}
}
TestModel.remoteMethod('testRemoteMethod',
//..rest of config

Related

Hubspot API - list of properties needed (nodejs)

I am integrating the hubspot API to track user interaction with our site. I am creating dynamic lists and I want to filter a user into a certain contact list by which URL they visit.
"filters": [
[
{
"operator": "CONTAINS",
"property": "hs_URL",
"value": `${id}`
},
],
]
I keep getting this error for all my attempts:
{"status":"error","message":"Couldn't find a Property with the given name 'hs_URL'","correlationId":"0723dcee-534b-4f92-9104-509d6885abbe","propertiesErrorCode":"PROPERTY_NOT_FOUND"},
I cannot seem to find a master property list and have tried many string combinations. Anyone familiar with hubspot property lists would be my savior.
Thank you~!
It's been a few months, so you may not need this anymore, but since I landed here while looking for a way to get all the properties from an object type in hubspot using nodejs, this might help others looking for the solution.
The master list of properties can be retrieved with the following API call:
const response = await hubspotClient.crm.properties.coreApi.getAll(objectType, false);
The arguments for getAll() expect:
objectType: a string, i.e "contacts".
archived: a boolean, i.e false. Set this true if you want to get archived properties.
The following code was adapted based on this page from the hubspot API docs:
https://developers.hubspot.com/docs/api/crm/properties
Once you're on the page, you can click on the "Endpoints" Tab to reveal code snippets for multiple environments, including nodejs.
For this example, getProperties(), retrieves all properties for a given object type. I used contacts for the object type, which I believe is where you are storing the url property, but you could use the same function to get properties for other object types such as companies or deals.
It might be worth noting that I mapped the results to return just the property names, which sounds like all you need for your case, but more information is contained in the results if you need it. Just remove this bit to get more information on each property:
.map(prop => prop.name)
const hubspot = require('#hubspot/api-client')
const hubspotClient = new hubspot.Client({ apiKey: "YOUR_API_KEY" })
const getProperties = async (objectType) => {
try {
const response = await hubspotClient.crm.properties.coreApi.getAll(objectType, false);
return response.body.results.map(prop => prop.name);
} catch (e) {
e.message === 'HTTP request failed'
? console.error(JSON.stringify(e.response, null, 2))
: console.error(e);
}
}
Here's an example for running the function to get a list of all property names for contacts.
(async () => {
var properties = await getProperties("contacts");
console.log(JSON.stringify(properties ,null,2));
})();
It took me a bit to find this, so figured I would post here in the hopes it saves time for someone else. This is the first time I've posted a solution, and I'm pretty new to this API and Hubspot in general, so feedback and/or better solutions are welcome. Cheers.

How can I only call my data source on time if I have two resolvers that rely on it with Apollo?

I have a restaurant query that returns info on my restaurant. The general information that most of my consumers want comes back from restaurant-general-info.com. There are additional fields though that my consumer might want to know, restaurant-isopen.com, which provides whether or not the restaurant is currently open and the hours that it is open.
I have written two property specific resolvers to handle isOpen and hours as show below:
type Query {
restaurant(name: String): Restaurant
}
type Restaurant {
name: String!,
address: String!,
ownerName: String!,
isOpen: Boolean!,
hours: String!
}
Query: {
restaurant: async(parent, {name}) => {
const response = await axios.get(`https://restaurant-general-info.com/${name}`);
return {
address: response.address
ownerName: response.owner
};
}
}
Restaurant: {
isOpen: async(parent) => {
const response = await axios.get(`https://restaurant-isopen.com/${name}`);
return response.openNow;
},
hours: async(parent) => {
const response = await axios.get(`https://restaurant-isopen.com/${name}`);
return response.hoursOfOperation;
}
}
The problem is that isOpen and hours share the same data source. So I don't want to make the same call twice. I know I could make a property like "open-info" that contains two properties, isOpen and hours, but I don't want the consumers of my graph to need to know / think about how that info is separated differently.
Is there anyway I can have a resolver that could handle multiple fields?
ex.
isOpen && hours: async(parent) => {
const response = await axios.get(`https://restaurant-isopen.com/${name}`);
return {
isOpen: response.openNow,
hours: response.hoursOfOperation
}
},
or is there some smarter way of handling this?
Note: The APIs are not real
This is a classic situation where using DataLoader is going to help you out a lot.
Here is the JS library, along with some explanations: https://github.com/graphql/dataloader
In short, implementing the DataLoader pattern allows you to batch requests, helping you to avoid the classic N+1 problem in GraphQL and mitigate overfetching, whether that involves your database, querying other services/APIs (as you are here), etc.
Setting up DataLoader and batching the keys you're requesting (in this case, openNow and hoursOfOperation) will ensure that the axios GET request will only fire once.
Here is a great Medium article to help you visualize how this works behind the scenes: https://medium.com/#__xuorig__/the-graphql-dataloader-pattern-visualized-3064a00f319f

node.js sails.js custom create beginner

I'm working with sails.js and mongo.db following some tutorials and creating a custom application and things are going well. Largely I'm using the built in, backbone I believe, scrud functions, I'm wondering how I could create a database entry from scratch. For example, the following works great from form data in my Student Controller:
create: function (req, res, next) {
Student.create(req.params.all(), function studentCreated(err, student) {
if (err) {
console.log(err);
req.session.flash = {
err: err
}
return res.redirect('/');
}
res.redirect('/');
});
},
For simplicity my Student model currently just has a first name and a one way association with a schools model.
module.exports = {
attributes: {
school: {
model: 'school'
},
first_name: {
type: 'string'
},
},
};
Say I wanted to, for the sake of understanding, just create a student with a fixed first name "Bob" and fixed school Id "xyz" in another action, without using the built in backbone create functions, nor using defaultTo in the model, nor using any form data. I would like to just code creating a database entry in an action, for example a test action in my Student controller. How would I go about this? I tried googling this a little, but given it's beginner nature I'm sure my query parameters were not particularly good.
If I correctly understand the question, I think you've already done most of the work. In the Student.create() call, replace req.params.all() by {first_name:'Bob', school:'xyz'}.
If you want to mix it with values from the request params, you could use {first_name:'bob', school: req.param('school')}

Loopback: Cannot call Notification.create(). The create method has not been setup. The PersistedModel has not been correctly attached to a DataSource

I am using Loopback and the push component. When calling Notification.create() I get the error:
Cannot call Notification.create(). The create method has not been setup.
The PersistedModel has not been correctly attached to a DataSource!
I'm just running the basic example server 2.0. In code I am trying to create a Notification. What's the problem?
I too got the same problem when trying to use login function of User model.
Got it fixed after an hour of hit and trial.
Answer: I extended User model to MyUser model (No coding inside this model, just used it as a wrapper) and inside Hotel.js (in my case a business class where i use to authenticate user before accessing hotel details) created a remoteMethod for login
code:
Hotel.auth=function(uname,pwd, cb)
{
Hotel.app.models.MyUser.login({username: uname, password: pwd}, function (err, token) {
if(err)
cb(null,err);
else
cb(null,token);
});
}
Hotel.remoteMethod(
'auth',
{
accepts:
[
{arg: 'uname', type: 'string',required: true},
{arg: 'pwd', type: 'string',required: true}
],
returns: {arg: 'Response Message', type: 'string'}
}
);
This works!
This one is pretty old, but just to put something up here. Without seeing your setup my guess is that the model you are using is not connected to any data source, or one that is not written properly. The default connector is in-memory and does implement this method correctly. Check your server/model-config.json file and find the entry for Notification and check what you have for the data source.

SailsJs/Express removing nested array `res.send()`

I'm using SailsJs (which is Express based) to send an JSON object with an array. For some reason, when I load the API in my browser, the array is not sent.
The code that sends the object is here:
exports.RESTifySend = function(res, objects) {
return RESTService.RESTify(objects).then(function(RESTedObjects) {
console.log("SENDING: ", RESTedObjects);
return res.json(RESTedObjects, 200); // I've also tried res.send()
}, function() {
res.send(500);
});
};
The logging statement SENDING: ... outputs:
SENDING: {
id: 'IKIlrgXhp6',
messages: [{
user: null,
text: 'trest',
sentAt: undefined
}]
}
The RESTifyService is just a small framework I built to remove object attributes that shouldn't be exposed in the API (passwords, emails, etc.).
Somewhere in the framework I built, I replaced toObject(); with lodash.clone([object]), which solved all the problems. For others experiencing a similar issue, I suggest trying the same thing in your toJSON method. The toObject(); method of waterline objects apparently has some odd side effects when you true to populate or edit an attribute that matches the name of an association.
Also ran into this issue when serializing a model with a collection attribute (like something with many Comments).
In my case I call .toJSON on the record object with the collection during serialization, which has a model definition like:
attributes: {
...
toJSON: function() {
var self = this.toObject()
// could pick a subset of attrs here
return _.pick(self, _.keys(self))
}
}
Which leaves me free to then populate the comments attribute and not have surprises about the data on the wire.

Resources