How to change loopback API response to a particular format - node.js

I am new to loopback I want to change every response from my loopback remote method API to a particular format
eg: if success
{
status:1,
data:{},
message:"Success"
}
If error
{
status:0,
data:{},
message:"Something went wrong"
}

You should create a boot script to change all remote method responses :
Create hook.js or any other name in /server/boot/
module.exports = function (app) {
var remotes = app.remotes();
// modify all returned values
remotes.after('**', function (ctx, next) {
if (ctx) {
ctx.result = {
status: 1,
data: ctx.result,
message: "Success"
};
} else {
var err = new Error();
next({
status: 0,
data: err,
message: "Something went wrong"
});
}
next();
});
};
Check these links for more information :
Formatting remote method responses (Last Section)
https://loopback.io/doc/en/lb3/Remote-methods.html
Hooks
https://loopback.io/doc/en/lb3/Strong-Remoting.html

Related

deleteMany only returns 1 value deleted in change streams

I have a deleteMany request but I am having a hard time in filtering my context of the deleteMany returned value. It only returns 1 value deleted from pusherjs.
Here is my change stream code and pusher code in server side;
if (schedules.operationType === 'delete') {
const scheduleDetails = schedules.documentKey;
pusher.trigger('schedules', 'deleted', {
_id: scheduleDetails._id,
teamOne: scheduleDetails.teamOne,
teamTwo: scheduleDetails.teamTwo,
user: scheduleDetails.user,
isDone: scheduleDetails.isDone,
isStarted: scheduleDetails.isStarted,
date: scheduleDetails.date,
gameEvent: scheduleDetails.gameEvent,
});
}
Here is my pusher code in client side. I am using React by the way. It is stored in my context api;
ScheduleChannel.bind('deleted', ({ deletedSchedule }) => {
console.log(deletedSchedule);
setScheduleList(
scheduleList.filter((schedule) => schedule._id !== deletedSchedule._id)
);
});
here is my code on request;
exports.deleteallmatch = async (req, res) => {
try {
const { sub } = req.user;
const deletedMatches = await Schedule.deleteMany({ user: sub });
return res.status(201).json({
message: 'All of your schedule is successfully deleted!',
deletedMatches,
});
} catch (err) {
return res.status(400).json({
message: 'Something went wrong.',
});
}
};
The delete request is fine but I want to have realtime in my app. Cuz it happened that only one data is being send instead of many. How can I solve this?
The deleteMany() method returns an object that contains three fields:
n – number of matched documents
ok – 1 if the operation was successful
deletedCount – number of deleted documents
What you can do is:
First find all elements that match your query
Store them in some variable
Perform deleting
Return the stored variable
let deleted_items = await Schedule.find({ user: sub });
await Schedule.deleteMany({ user: sub });
return res.status(201).json({
message: 'All of your schedule is successfully deleted!',
deleted_items,
});

Not able to create elastic search index using nodejs

I tried creating elasticsearch index in node.js but I always get the exception:
status: 405,
displayName: 'MethodNotAllowed',
message:
'Incorrect HTTP method for uri [/anil] and method [POST], allowed: [DELETE, PUT, GET, HEAD]' } { error:
'Incorrect HTTP method for uri [/anil] and method [POST], allowed: [DELETE, PUT, GET, HEAD]',
status: 405 } 405
Search, getmapping, index exists functions are working fine but not the create index.
(function () {
const elasticsearch = require('elasticsearch');
const esClient = new elasticsearch.Client({
host: '127.0.0.1:9200',
log: 'error'
});
const indices = function indices() {
esClient.indices.exists({
index: 'anil'
}, (err, res, status) = > {
if (res) {
console.log('index already exists');
} else {
//tring to create index but not working
esClient.indices.create({
index: 'anil'
}, (err, res, status) = > {
console.log(err, res, status);
})
}
});
};
// only for testing purposes
// all calls should be initiated through the module
const test = function test() {
console.log(`elasticsearch indices information: `);
indices();
};
test();
module.exports = {
indices
};
}());
Please help how can I create the elasticsearch index using nodejs
Try to change elasticsearch configurations by adding these lines then restart elasticsearch and try again:
http.cors.enabled: true
http.cors.allow-origin: "*"
http.cors.allow-methods: OPTIONS, HEAD, GET, POST, PUT, DELETE

Execute push notification after sending message

I am using sails.socket for message sending. My requirement is that, I have to send a push notification after the success of sent message. How it is possible. Please see the code that I have written.
sendChatMessage(chatMessage, function() {
// calling push notification when a chat message send
var serverKey = req.options.settingsKeyValue.PUSH_SERVER_KEY;
var typeData = { type:1, data:{type:1} };
var pushData = { title:'New Message', body: data };
pusherService.pushFcm(serverKey,typeData,pushData,toId, function(err, result) {
if(err) {
return res.json(200, {status:1, status_type: 'Success', message: 'Error in sending the message'});
}else{
return res.json(200, {status:1, status_type: 'Success', message: 'You have successfully send the message'});
}
});
});
function sendChatMessage(){
var socketRoom = "userRoom_"+toId;
var roomsSubcribers = sails.sockets.subscribers(socketRoom);
console.log("roomsSubcribers");
console.log(roomsSubcribers);
var data = {
text: message,
from_id: fromId,
from_name: userResult[0].firstName+' '+userResult[0].lastName, from_img : userResult[0].profilePhoto,
};
sails.sockets.broadcast(socketRoom,{
type : "chat",
message : data,
});
callback(chatMessage);
}
These blocks of code look ok to me... you just have to position them in the right place within your application. Have you tried this?
You definitely need to name some of your arguments in both the sendChatMessage function definition, and in your callback function.
Maybe you need something like this:
// in some controller
// notice the named arguments 'options' and 'callback'
var sendChatMessage = function(options, callback){
var socketRoom = "userRoom_"+options.toId;
var roomsSubcribers = sails.sockets.subscribers(socketRoom);
console.log("roomsSubcribers");
console.log(roomsSubcribers);
var data = {
text: options.message,
from_id: options.fromId,
from_name: options.fromName,
from_img : options.fromImg,
};
sails.sockets.broadcast(socketRoom,{
type : "chat",
message : data,
});
callback(data);
};
module.exports = {
someMethod: function(req, res) {
// do some work, including defining / getting all options
var options = {
toId: 123,
fromId: 456,
message: 'test message',
fromName: 'test user',
fromImg: 'some/image.jpg' // don't know if you need a source here or what
};
// invoke your function - notice the named argument in the callback
sendChatMessage(options, function(data) {
// calling push notification when a chat message send
var serverKey = req.options.settingsKeyValue.PUSH_SERVER_KEY;
var typeData = { type:1, data:{type:1} };
var pushData = { title:'New Message', body: data };
pusherService.pushFcm(serverKey,typeData,pushData,toId, function(err, result) {
if(err) {
return res.json(200, {status:1, status_type: 'Success', message: 'Error in sending the message'});
}else{
return res.json(200, {status:1, status_type: 'Success', message: 'You have successfully send the message'});
}
});
});
},
};
I can't know if this is the right way to use all your plugins, etc, but putting the pieces together seems to make sense.
Last note, when you define your own callbacks, it's good practice to make the first argument an error object, and check for received errors in the callback body. When successful you can return a null error, like callback(null, data);.
Hope this is helpful.

Why can't Restful pass body into database

I'm creating a RESTful API.
I wanna use GET method to check if lastName exists. If it can find lastName, return "YES", otherwise, call a POST method to create a data with lastName entered.
The problem is that it can create a new data, but the body is empty. Ideally, it should contain a value with lastName, like "lastName": "James",
{
"_id": "58a22c3c3f07b1fc455333a5",
"__v": 0
}
Here is my code.
router.route("/findLastName/:id")
.get(function(req,res){
var response;
mongoOp.findOne({deviceID: req.params.id}, function(err, result){
if (err) {
response = {"error" : true,"message" : "Error fetching data"};
res.json(response);
}
if (result) {
response = "YES";
res.send(response);
} else {
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
var xhr = new XMLHttpRequest();
var POSTurl = "http://localhost:6002/users";
var params = "lastName=" + req.params.id;
xhr.open("POST", POSTurl, true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send(params);
}
});
})
PS: GET method works well, not a issue.
Let me modify a bit of your code and add comments as pointers:
// changed findLastName to find-last-name. It's a common convention,
// urls need to be case insensitive. It doesn't concern lastName, as
// that's a parameter, internal to your app so it's fine.
// even better if you name the route `find-or-create` or something, to better
// reflect what you're doing.
router.route("/find-last-name/:lastName")
.get(function(req,res){
var response;
mongoOp.findOne({deviceID: req.params.lastName}, function(err, result){
if (err) {
response = {"error" : true,"message" : "Error fetching data"};
// Adding a `return statement here. If you don't return, you'll tell
// the user that there was an error, but your code continues running
// potentially calling that res.json twice.
// Also, since it's an internal error, it's common to tell the client
// about it, by setting the status to 500
return res.status(500).json(response);
}
if (result) {
// turning the message to JSON instead. You started that above,
// and for the sake of your clients (your frontend), it's
// better to stick to JSON. Also you can pass useful info, such as
// _id of the document.
// Again adding a `return` here, and then the rest of the code
// is nested one level less. not required, but some people like that.
response = {
message: "Last name exists."
};
return res.json(response);
}
// Here begins the new code. I'm typing what I can infer from your code,
// I don't know if your MongoDB driver looks like that exactly.
mongoOp.insert({
deviceId: req.params.lastName
// add other optional properties here.
}, function (err, response) {
if (err) {
var message = {
error: true,
message: 'Cannot save new entry.'
}
return res.status(500).json(message);
}
// if we're here, everything went ok. You can probably return
// the _id of the given user.
return res.json({
message: 'New user created.',
_id: response._id
});
});
});
})

How to make a block of code work synchronously

Here is my code :
server.get(url_prefix + '/user/:user_id/photos', function(req, res, next) {
if (!req.headers['x-session-id']) {
res.send({
status: {
error: 1,
message: "Session ID not present in request header"
}
})
} else {
User.findOne({
session_id: req.headers['x-session-id']
}, function(err, user) {
if (user) {
var user_id = req.params.user_id
Album.find({userId : user_id})
.populate('images')
.exec(function (err, albums) {
if (albums) {
albums.forEach(function(album, j) {
var album_images = album.images
album_images.forEach(function(image, i) {
Like.findOne({imageID : image._id, userIDs:user._id}, function(err,like){
if(like){
albums[j].images[i].userLike = true;
}
})
})
})
return res.send({
status: {
error: 0,
message: "Successful"
},
data: {
albums: albums
}
})
} else
return notify_error(res, "No Results", 1, 404)
})
}
else {
res.send({
status: {
error: 1,
message: "Invalid Session ID"
}
})
}
})
}
})
I am trying to add a extra value (albums[j].images[i].userLike = true;) to my images array, which is inside album array.
The problem is return res.send({ send the data before we get response from the foreach
How can I make it work, so that return should happen only after foreach has completed all the iteration
You will have to wait with invoking res.send until you fetched all the likes for all the images in each of the albums. E.g.
var pendingImageLikes = album_images.length;
album_images.forEach(function(image, i) {
Like.findOne({imageID : image._id, userIDs:user._id}, function(err,like){
if (like) {
albums[j].images[i].userLike = true;
}
if (!--pendingImageLikes) {
// we fetched all likes
res.send(
// ...
);
}
});
You might need to special case for album_images.length === 0.
Also, this does not take into account that you have multiple albums with multiple images each. You would have to delay res.send there in a very similar way to make this actually work. You might want to consider using a flow control library like first (or any other of your preference, just search for "flow control library") to make this a bit easier.
Also, you might want to consider not relying on semicolon insertion and manually type your semicolons. It prevents ambiguous expressions and makes the code easier to read.
Since you need your code to wait until all of the find operations have completed, I'd suggest you consider using the async package, and specifically something like each (reference). It makes using async loops cleaner, especially when dealing with MongoDB documents and queries. There are lots of nice features, including the ability to sequentially perform a series of functions or waterfall (when you want to perform a series, but pass the results from step to step).
> npm install async
Add to your module:
var async = require("async");
Your code would look something like this:
albums.forEach(function(album, j) {
async.each(album.images, function(album, done) {
Like.findOne({imageID: image._id, userIDs:user._id}, function(err, like){
if(!err && like){
albums[j].images[i].userLike = true;
}
done(err); // callback that this one has finished
})
})
}, function (err) { // called when all iterations have called done()
if (!err) {
return res.send({
status: {
error: 0,
message: "Successful"
},
data: {
albums: albums
}
});
}
return notify_error(res, "No Results", 1, 404);
});
});

Resources