my delete method of axios didn't work while post method work - mongodb

I am making likes, dislikes function.
everything is working fine, except this delete method 😐
at React :
const goDownLike = async () => {
const variables = {
fromWhom: user.userData._id,
toWhat: toWhat
};
await axios.delete('/api/heart/downLike', variables);
}
at Node.js :
router.delete('/downLike', (req, res) => {
Like.findOneAndDelete(req.body).exec((err, result) => {
if (err) return res.status(400).json({ success: false, err });
res.status(200).json({ success: true });
});
});
when I clicked heart everytime, it seemed like it deleted somebody's likes including mine 😐
so I changed like this:
router.delete('/downLike', (req, res) => {
const { fromWhom, toWhat } = req.body;
Like.findOneAndDelete({ fromWhom: fromWhom, toWhat: toWhat }).exec(
(err, result) => {
if (err) return res.status(400).json({ success: false, err });
res.status(200).json({ success: true });
}
);
});
but this is actually do not delete any likes, even mine!
I changed from this
Like.findOneAndDelete
to this
Like.deleteOne
and this is not working, too. it delete nothing.
so I changed delete method to post method
and finally it worked. 😐
my likes was deleted and somebody's heart was not.
but I don't want to use post method instead of delete method 😐
I want to keep the rules of RESTful.
What should I do?
What did I missed?
thanks for your helps.

The reason it works with POST and not DELETE is that the second parameter for axios.post() is the body, whereas the second parameter for axios.delete() is the axios config object. This is presumably because there has been some disagreement and change over time as to whether DELETE requests can or should have a body, and what should happen if they do.
You can pass data in the config object, as seen here.

Related

Axios Delete Not Working In React App But Working In PostMan/Insomnia

Axios DELETE works when I send a request through postman but on my react app it doesn't. I'm passing the _id that MongoDB assigns the entry. I'm initiating ObjectId and it still doesn't work. I also double checked if I was using the correct route, which I was.
In my app I have click function that calls SaveBook. That part I feel okay about. Let me know if I need to share something else.
SaveBook in AuthActions.js on the front end
export const saveBook = ({books, user, book, _id}) => {
return function () {
console.log(`This is id ${JSON.stringify(_id)}`)
const savedIndex = books.indexOf(book);
if (savedIndex >= 0) {
console.log(savedIndex)
axios
.delete("/api/users/wishlist", {_id})
} else {
console.log(savedIndex)
// console.log(`Adding ${book.book.title} to faves...`);
axios
.post("/api/users/dashboard", {book, user})
.then(console.log("success"))
.catch (err =>
json(err)
);
}
}
};
In users.js the delete operation on the server side
router.delete('/wishlist', (req, res) => {
const db = mongoUtil.getDb();
db.db("mern-auth-2").collection("savedbooks")
.deleteOne({_id:ObjectId(req.body._id)})
.then(res.json(res.data))
});
I realized req.body wasn't the correct choice for the Delete method and used url params/ req.params to send the _id. This works well.
Fixed this line in authActions.js
axios
.delete("/api/users/wishlist/" + _id,)
Fixed these few lines in Users.js
router.delete('/wishlist/:id', (req, res) => {
const db = mongoUtil.getDb();
db.db("mern-auth-2").collection("savedbooks")
// .deleteOne({_id:ObjectId(req.body._id)})
.deleteOne({_id:ObjectId(req.params.id)})

Google Action Webhook Inline Editor Returns Before the API call

This is my first Google Action project. I have a simple slot after the invocation. User enters the value on prompt and slot invokes the webhook and make a call to API using the user input. All works fine. However the webhook returns to users even before the API call finish processing and returns the value (line 1 conv.add). I do see in the logs that everything from API is logged fine after the webhook returns to user. Below is the code I am using. I am using inline editor. What am I missing? Thanks for help in advance.
const { conversation } = require('#assistant/conversation');
const functions = require('firebase-functions');
var https = require('https');
const fetch = require('node-fetch');
const app = conversation({debug: true});
app.handle('SearchData', conv => {
const body = JSON.stringify({
val: "this is my body"
});
// prepare the header
var postheaders = {
'Content-Type' : 'application/json',
'Auth' : 'MyAuthCreds'
};
fetch('https://host.domain.com/data', {
method: 'post',
body: body,
headers: postheaders,
})
.then(res => res.json())
.then(d => {
console.log(d);
var profile = d;//JSON.parse(d);
console.log(d.entries);
console.log("Length: "+ d.entries.length);
if(d.entries.length > 0)
{
console.log("Data found");
conv.add("Data found"); //line 1
}
else
{
console.log("no data found");
conv.add("no data found"); //line 1
}
})
.catch(function (err) {
// POST failed...
console.log(err);
});
});
exports.ActionsOnGoogleFulfillment = functions.https.onRequest(app);
Your issue is that your handler is making API calls which are asynchronous, but the Assistant Conversation library doesn't know that you're doing so. So as soon as the handler finishes, it tries to send back a response, but your asynchronous responses (the stuff in the then() blocks) haven't executed yet.
To address this, you need to return a Promise object so the library knows to wait till the Promise is fulfilled before it returns.
Fortunately, in your case, this should be pretty straightforward. fetch and all the .then() blocks return a Promise. So all you need to do is add a return statement in front of the call to fetch. So something like this:
return fetch('https://host.domain.com/data', {

Url as req.params in Express

I'd like to use express and mongo DB to find a document based off of a URL.
My Schema includes
bandUrl: {
type: String
}
This is my Rout inside of the Express server.
// Get Single Band By bandUrl
router.get('/bandUrl/:url', (req, res) => {
quoteGenerator.find({bandUrl: req.params.url}).then(gen => res.json(gen))
})
I set one of the documents to have bandUrl as 'http://localhost:3000/'.
I tested the route using something other than a URL - just using a string works fine... I'd really like to use the URL though. Is there a way to do this?
Here is my fake/test route form the application..
const getFakeInfo = async () => {
try {
const response = await fetch(`/api/autoquotegenerators/bandUrl/http://localhost:3000/"`, {
method: 'GET',
})
const responseData = await response.json()
console.log(responseData)
} catch (error) {
console.log(error)
}
}
I am thinking the extra slashes in the URL are whats causing the issue.
Thanks for your help!
What you want to you use is encodeURIComponent(). This function escapes all URI specific characters, so they get interpreted properly. Your request code should look something like this:
const response = await fetch(`/api/autoquotegenerators/bandUrl/${encodeURIComponent("http://localhost:3000/")}`, {
method: 'GET',
})
If you want to learn more about this function, you can have a look over here.

How to manage self created error message instead of using default celebrate #hapi/joi code

I have two files, one is api.js and other one is handler.js. For schema handling I am using celebrate module #hapi/joi
On api.js I wrote only the API name
On handler.js I wrote the API functionality.
api.js
//JOI Schema Validator Middleware.
router.use(celebrate({
body: Joi.object().keys({
post: Joi.string().max(10),
userid: Joi.string(),
})
}));
const handler = require('./handler');
router.post('/createpost', handler.createPost);
router.use(errors());
module.exports = router;
By this if error happens then i got the Response like this
{"statusCode":400,"error":"Bad Request","message":"child \"post\" fails because [\"post\" length must be less than or equal to 10 characters long]","validation":{"source":"body","keys":["post"]}}
I just want to Convert this error into my own format error i.e something like this
{error: true, status: 500, message: 'validation error', version: x.x.2}
The default joi error is generated through router.use(errors()); this module. How I modify this?
Any help or suggestion is really appreciated.
TL;DR: Create your own 'errors()' function.
You have probably managed to change it by now, but just like me, I had the exact same issue and found this answerless thread.
Well, for future readers, celebrate errors() is nothing else than a function, more exactly, this one:
(err, req, res, next) => {
// If this isn't a Celebrate error, send it to the next error handler
if (!isCelebrate(err)) {
return next(err);
}
const {
joi,
meta,
} = err;
const result = {
statusCode: 400,
error: 'Bad Request',
message: joi.message,
validation: {
source: meta.source,
keys: [],
},
};
if (joi.details) {
for (let i = 0; i < joi.details.length; i += 1) {
const path = joi.details[i].path.join('.');
result.validation.keys.push(EscapeHtml(path));
}
}
return res.status(400).send(result);
}
There, you can see the response object 'result' being declared and how it's done. So, to change the output of it, you have to not use errors() and create your own function to handle it.
So, I declared a new function:
private errorHandling = (err, req, res, next) => {
if (isCelebrate(err)) {
return res.send({
statusCode: 400,
message: err.joi.message
});
}
return next(err);
}
You can obviously change the above to suit your needs.
Update
Celebrate changed their error structure to a CelebrateError, now you need access the error details using:
const errorBody = err.details.get('body'); // 'details' is a Map()
const { details: [errorDetails] } = errorBody;
instead of the err.joi. The message property remains the same.
Then, instead of using app.use(errors()) I used app.use(this.errorHandling), and now I get the celebrate response formatted as I want to.
After some research, I found out it can be solved 2 ways:
[Segments.BODY]: Joi.object().keys({
value: Joi.string().required().error(new Error('Value is required and has to be a text!')),
})
or
[Segments.BODY]: Joi.object().keys({
password: Joi.string().required().pattern(new RegExp('^[a-zA-Z0-9]{3,30}$')).min(8).label('Password').messages({
'string.pattern.base': 'Your {#label} does not matche the suggested pattern',
'string.base': `Your {#label} should match the suggested pattern`,
'string.empty': `Your {#label} can not be empty`,
'string.min': `Your {#label} has to be at least {#limit} chars`,
'any.required': `Your {#label} is required`,
}),
})

Baasbox and Javascript

I'm trying BaaSbox, a free Backend as a Service. But it has no out-of-the-box Javascript support I can use right away (yet, only iOS and Android)
I'm having trouble sending the right curl command from javascript, anyone happen to know a good resource or a simple working $.ajax template? I've tried a few examples from stackoverflow, but none of them specifically aimed at BaaSbox.
I've tried following the Java instructions on their site here. Just making a simple login work, but I keep getting the wrong responses from the server.
Or on the other hand, anyone know a good, free alternative to BaaSbox? I just want to be able to install it on my own server, no paid plans or whatever.
in the download page there is a preliminary version of the JS SDK (added few days ago).
The documentation is on the way, however in the zip file you can find a simple example.
For example to perform a signup:
//set the BaasBox parameters: these operations initialize the SDK
BaasBox.setEndPoint("http://localhost:9000"); //this is the address of your BaasBox instance
BaasBox.appcode = "1234567890"; //this is your instance AppCode
//register a new user
BaasBox.createUser("user", "pass", function (res, error) {
if (res) console.log("res is ", res);
else console.log("err is ", error);
});
Now you can login into BaasBox
//perform a login
$("#login").click(function() {
BaasBox.login("user", "pass", function (res, error) {
if (res) {
console.log("res is ", res);
//login ok, do something here.....
} else {
console.log("err is ", error);
//login ko, do something else here....
}
});
Once the user is logged in he can load the Documents belonging to a Collection (the SDK automatically manages the Session Token for you):
BaasBox.loadCollection("catalogue", function (res, error) { //catalogue is the name of the Collection
if (res) {
$.each (res, function (i, item) {
console.log("item " + item.id); //.id is a field of the Document
});
} else {
console.log("error: " + error);
}
});
However under the hood the SDK uses JQuery. So you can inspect it to know how to user $.ajax to call BaasBox.
For example the creatUser() method (signup) is:
createUser: function (user, pass, cb) {
var url = BaasBox.endPoint + '/user'
var req = $.ajax({
url: url,
method: 'POST',
contentType: 'application/json',
data: JSON.stringify({
username: user,
password: pass
}),
success: function (res) {
var roles = [];
$(res.data.user.roles).each(function(idx,r){
roles.push(r.name);
})
setCurrentUser({"username" : res.data.user.name,
"token" : res.data['X-BB-SESSION'],
"roles": roles});
var u = getCurrentUser()
cb(u,null);
},
error: function (e) {
cb(null,JSON.parse(e.responseText))
}
});
}