Here is the code for my server which works fine. I am trying to achieve this with netlify's serverless functions which I have pasted further below.
CODE ON STANDARD SERVER-HEROKU
const ratingController = {};
const Rating = require("../models/ratingModel");
ratingController.getAllRatings = async function (req, res) {
const rating = await Rating.find();
res.status(200).json({
status: "success",
data: rating,
});
};
ratingController.createOneRating = async function (req, res) {
console.log(req.body);
req.body.userIp = req.headers["x-forwarded-for"];
const rating = await Rating.create(req.body);
// const rating = new Rating(req.body);
// await rating.save();
res.status(200).json({
status: "success",
data: {
rating,
},
});
};
PART 1 - GET REQUEST
Here's my code for the getAllRatings and it works fine
SERVERLESS FUNCTION - NETLIFY
const { MongoClient } = require("mongodb");
require("dotenv").config();
exports.handler = async function getData(event, context) {
const client = await MongoClient.connect(process.env.DB, {
useUnifiedTopology: true,
useNewUrlParser: true,
});
const db = client.db();
try {
const slug = event.queryStringParameters.id;
const data = await db.collection("collectionName").find({ slug }).toArray();
client.close();
return {
statusCode: 200,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
status: "success",
data: data,
}),
};
} catch (error) {
console.log(error);
return {
statusCode: 400,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
status: "fail",
message: error.message,
}),
};
}
};
My first question for the above is
Just because it works may not mean it's the right way to do it.. I had a few concerns if calling the database each time there's a call is correct and then placing the code the way I have, if it's the way it should be. It's all based on testing and random research. There's no real method being followed so would appreciate some guidance on a more efficient method to do this.
Normally on a regular server the database connection is done just once.. and here I seem to be doing it every time and I am a bit confused if that's ok or not..
PART 2 - POST REQUEST
Here's my code for the POST request createOneRating
SERVERLESS FUNCTION - NETLIFY
const { MongoClient } = require("mongodb");
require("dotenv").config();
exports.handler = async function createRating(event, context) {
const client = await MongoClient.connect(process.env.DB, {
useUnifiedTopology: true,
useNewUrlParser: true,
});
const db = client.db();
try {
console.log(event);
const rating = await db.collection("ratings").insertOne(event.body);
client.close();
return {
statusCode: 200,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
status: "success",
data: rating,
}),
};
} catch (error) {
console.log(error);
return {
statusCode: 400,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
status: "fail",
message: error.message,
}),
};
}
};
This one does not work as it says
{
"status": "fail",
"message": "Cannot create property '_id' on string ''"
}
And I guess that's because event.body is not where the data is.. but I am not sure how to get the POST data in a serverless deployment.
So my second question is
How do I retrieve the data sent by a POST request. As there's no request parameter I am a bit confused.
Also I'd like to add the IP of the user so other than the POST data I'd also need some help on how to do this
req.body.userIp = req.headers["x-forwarded-for"];
Based on my own research, I have answers to the questions and am placing them here for my own reference and for those who might face a similar situation in the future.
Question 1 : Is it ok to make a database connection on every call that's made to a serverless function
It seems it's ok to do this and for those, like me, who thought that maybe it was not the right way to do it, it's definitely not the wrong way. Maybe there's an efficient way to do this and I'd be open to learn more about this if possible. But for now, it's good to know that it's not wrong to connect to the database each time you make a call.
Question 2: How to make a POST request on a serverless as there's no request parameter
I was not aware that the event parameter is in fact a replacement for the request parameter and that the headers and body are properties of the event object and can be accessed in the same way ie event.body and event.headers. Here's a link that could save you some time to confirm this.
(https://docs.netlify.com/functions/build-with-javascript/#synchronous-function-format)
And if you, like me, don't know if a serverless function can be defined as GET or POST or run into an issue where the POST request gets converted into a GET when making a function call here's a link that would help.
How to define Netlify function endpoint as POST?
Related
This question already has an answer here:
Express routes parameters
(1 answer)
Closed last month.
I'm trying to send a POST to my server, in order to edit a user's details. I've made sure it's sending to the right URL, however get a 404 response. GET requests work fine, however my POST doesn't seem to get through for whatever reason. I've been searching for solutions for a while, with no luck hence posting here!
user.js (server side)
userRoutes.route('/user/update/:id').post(function (req, response) {
let db_connect = dbo.getDb("preview");
let myquery = { _id: ObjectId(req.params.id) };
let newValues = {
$set: {
name: req.body.name,
user_name: req.body.user_name
},
};
db_connect
.collection("users")
.updateOne(myquery, newValues, function (err, res) {
if (err) throw err;
console.log('user updated');
response.json(res);
})
});
middleware
export const updateUser = async (id, userDetails) => {
const endpoint = `${serverIp}/user/update/?id=${id}`;
try {
const response = await fetch(endpoint, {
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(userDetails)
})
const result = await response.json();
return result;
} catch (error) {
console.log(error)
}
}
and a simple function to handle submitting
function handleSave() {
const newUserDetails = {
name: accountHolder,
user_name: accountUsername
};
updateUser(userId, newUserDetails);
}
Under networking in dev tools I can see the URL is indeed correct, so I can't see why this isn't working
chrome dev tools
Any help would be greatly appreciate it!
I've tried sending a basic response (i.e. a string instead of object), changing the endpoint, and more all to no avail
It seems like you are passing the id as a query param and not as part of the path
const endpoint = `${serverIp}/user/update/?id=${id}`;
^
What I can see from first glance is that in server-side you are using request parameter for id, but in the client you're sending id as a request query
I'm trying to pass some datas from frontend to backend.
Here the code lines....
FRONTEND
handleSubmit = (event) => {
alert('A form was submitted: ' + this.state);
fetch('http://localhost:5000/store-data', {
method: 'POST',
/*mode: 'cors',*/
headers: {
'Content-Type': 'application/json',
},
// We convert the React state to JSON and send it as the POST body
body: JSON.stringify(this.state)
}).then(function(response) {
console.log(response)
return response.json();
});
console.log("Sono entrato");
event.preventDefault();
}
BACKEND
app.post("/store-data", (req, res) => {
let data = { name: req.body.username };
console.log(data.name);
console.log(JSON.stringify(req.body));
res.json("Hello");
});
All the console.log works well.
But if I go on the "localhost:5000/store-data", I get this error:
"CANNOT GET /store-data/ PAGE NOT FOUND 404"
Thanks
I add tis code and now seems to work:
app.get("/store-data", (req, res) => {
let data = { name: req.body.username };
console.log(data.name);
console.log(JSON.stringify(req.body));
res.send("Hello");
});
But I have to keep both the app.get() and the app.post() to work.
I think that is not the correct way.
Thanks
I solve it. The problem was that i make a mistake with the fetch method and I get undefined in the return response.json.
Now in the post method res.send work.
It was an error on the Frontend code not in the Backend.
In my case I don't need anymore app.get(....), but only the app.post(.....).
I am trying to use Zoho Creator's ADD RECORDS DATA API (https://www.zoho.com/creator/help/api/v2/add-records.html)
And here's the URL to help you understand my issue. The URL below will show you a JSON of all the variables involved before I run the POST method.
https://vp-expo-node-server.herokuapp.com/eticket/
This above link will show you the result of this controller
exports.addOneExhibitorToCreator = async function(req, res, next) {
try {
const token = await getAccessToken();
const url = process.env.ZOHO_CREATOR_FORM_URL + "/Add_Organisation";
// const organisation = req.body;
const organisation = {
data: {
isActive: true,
Organisation_Name: "Test With Alim",
Type: "Exhibitor",
Short_Name: "test",
Email: "test#fourplusmedia.com",
},
};
const options = {
Method: "POST",
Headers: {
"Content-Type": "application/json",
Authorization: "Zoho-oauthtoken " + token,
},
body: JSON.stringify(organisation),
};
const functionForResponse = "const response = await fetch(url, options);";
// const response = await fetch(url, options);
// const data = await response.json();
res.status(200).json({
status: "success",
token,
options,
url,
organisation,
functionForResponse,
});
} catch (err) {
console.log(err);
res.status(500).json({
err,
});
}
};
When I uncomment these 2 lines in the above controller
const response = await fetch(url, options);
const data = await response.json();
I get this result
https://vp-expo-node-server.herokuapp.com/eticket/response
As I don't know how to display the error on the browser I tried to console.log it and I got this error in the console
TypeError: Request with GET/HEAD method cannot have body
at new Request (/Applications/MAMP/htdocs/vp-expo-node-server/node_modules/node-fetch/lib/index.js:1199:10)
at /Applications/MAMP/htdocs/vp-expo-node-server/node_modules/node-fetch/lib/index.js:1409:19
at new Promise (<anonymous>)
at fetch (/Applications/MAMP/htdocs/vp-expo-node-server/node_modules/node-fetch/lib/index.js:1407:9)
at exports.addOneExhibitorToCreatorResponse (/Applications/MAMP/htdocs/vp-expo-node-server/controllers/eticketController.js:82:28)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
And I can confirm that the this line in the console.log
at exports.addOneExhibitorToCreatorResponse (/Applications/MAMP/htdocs/vp-expo-node-server/controllers/eticketController.js:82:28)
points to the uncommented lines...
So there's something wrong I am doing in those 2 lines.. but according to me it's the right way to send a POST request.. and I have no clue how a POST request can get a Request with GET/HEAD method cannot have body error.
Any help would be appreciated. I've double checked it and I've even asked Zoho for help (they mentioned as it's a client side thing they couldn't do much to help)
I've got my answer in the comments but just posting an answer just in case someone else is facing 'REQUEST WITH GET/HEAD METHOD' error when trying to send a POST request.
As #CherryDT pointed out in my case the POST method was not being used as I had misspelt 'method' as 'Method' (and I had committed the same mistake with 'headers' as 'Headers')..
So the 'method' property was not being used at all and it was defaulting to 'GET'... and hence the error.
I am using a fetch() request from the client to PUT updated user data to the backend, to then be saved into a DB. So far, the all of the route is working fine, verified and tested in Postman.
In this User Update route, there is an if statement that checks for an error when searching for the user in the database, and if this error is thrown, it sends a response of 404 and a message to the client.
When I make the fetch() request from the client to this route, regardless if there is an error, the response is always a status 200, and does not include any response from my route. I need the client to be able to handle the potential errors the routes might produce. For example with this user update route, if the user is not found in the database for whatever reason, an error and message is returned, so the client needs to know this.
Here is some code:
Client-side:
fetch(`http://localhost:3000/users/${userId}`, {
method: "put",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify(userData)
}).then(response => console.log(response))
.catch(err => console.log(err));
Here in the client, I am using console.log() to visualize everything. When the response returns, I get back:
Response {type: "basic", url: "http://localhost:3000/users/accounts/", redirected: false, status: 200, ok: true, …}
Server-side route controller:
exports.user_update = (req, res) => {
const { params, body } = req;
const { userid } = params;
User.findByIdAndUpdate({ _id: userid }, body, { new: true }, (err, user) => {
if (err)
res.send({
status: 404,
message:
"There was an issue finding and updating the user on the server."
});
else
res.send({
status: 200,
user
});
});
};
Now here on the server, I anticipated the response of the fetch() to be either the 404 error or the 200 success along with their payloads. Neither are returned in a response from the server. Instead as mentioned above, I am getting a generic 200 ok response simply letting me know the fetch() made a connection to the route. This route(along with the others) has been tested in Postman, and all work as anticipated returning the expected responses.
What am I not understanding here? Is my idea of using a fetch() request in this manner wrong? I feel like I might be close, but that's just my ignorant guess. Thank for reading!
I figured out the solution.
The fetch request was wrong, here is the update:
fetch(`http://localhost:3000/users/${userId}`, {
method: "put",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify(userData)
}).then(response => response.json())
.then(response => console.log(response))
.catch(err => console.log(err));
I had to call response.json() to parse the response as a JSON object.
According to express documentation you sent status as a field of JSON. To correctly send http status replace your res.send(...) with
res.status(404).send("There was an issue finding and updating the user on the server.");
and
res.send(user);
You are sending the status field inside your payload, which is not parsed by fetch API.
To solve this you could something as below
exports.user_update = (req, res) => {
const { params, body } = req;
const { userid } = params;
User.findByIdAndUpdate({ _id: userid }, body, { new: true }, (err, user) =>
{
if (err)
res.status(404).send({
message: "There was an issue finding and updating the user on the server."
});
else
res.status(200).send({
user
});
});
};
I'm trying to send a POST request locally with a username and password in the body through Axios.
I'm deploying a Flask app on http://127.0.0.1:5000/login, which handles the /login route. The POST request fails with the following error
POST http://127.0.0.1:5000/login 500 (INTERNAL SERVER ERROR)
Error: Request failed with status code 500
at createError (createError.js:16)
at settle (settle.js:18)
at XMLHttpRequest.handleLoad (xhr.js:77)
I researched a bit and thought it might be a problem with CORS, but this doesn't seem to be the case because I tried an Axios GET request and it worked fine (response logged properly). Here's part of my code
axios.get("http://127.0.0.1:5000").then(function(response) {
console.log(response);
}).catch(function(error) {
console.log(error);
})
axios.post("http://127.0.0.1:5000/login", {
username: this.state.username,
password: this.state.password
}).then(function(response) {
console.log(response);
}).catch(function(error) {
console.log(error);
})
Looking at Chrome DevTools, I can see that the POST request payload is properly populated. I then tried printing out the keys server-side in the Flask app using the following code, but I got nothing, empty. (which was expected since the POST request failed)
dict = request.form
for key in dict:
print('form key '+dict[key])
HOWEVER using Postman with the corresponding keys and values works properly and returns a response and prints out the keys (see above). Where is the failure coming from? Why would the POST request fail when a GET seems to work just fine?
Feb 2021. Wasted 2 hours on this. Not much help on this famous library on internet.
Solution:
In the catch block, the error which will always be 500 internal server error
so, use error.response.data instead of error.
Code:
try {
let result = await axios.post( // any call like get
"http://localhost:3001/user", // your URL
{ // data if post, put
some: "data",
}
);
console.log(result.response.data);
} catch (error) {
console.error(error.response.data); // NOTE - use "error.response.data` (not "error")
}
Update:
I ended up writing a common function for handing error:
File: common.app.js
export const errorUtils = {
getError: (error) => {
let e = error;
if (error.response) {
e = error.response.data; // data, status, headers
if (error.response.data && error.response.data.error) {
e = error.response.data.error; // my app specific keys override
}
} else if (error.message) {
e = error.message;
} else {
e = "Unknown error occured";
}
return e;
},
};
More info: https://github.com/axios/axios#handling-errors
So I also got stuck in the same problem and the solution that I found was something like this :
let data = JSON.stringify({
username: this.state.username,
password: password
});
const response = axios.post(url,data,{headers:{"Content-Type" : "application/json"}});
This solution worked for me.
Apparently Axios didn't take kindly to the raw JSON object
{username: this.state.username, password: password}
but passing the data into a FormData object seemed to work just fine!
After working 2 hours, I realized I made a mistake about the body and data. So, in the axios make sure you pass the data like this.
async function loadToken(){
try{
response = await axios({
url: ``,
headers: {
'Authorization': '',
'Content-Type': '',
},
data: '',
method: 'POST'
});
let data = response.data;
return {
tokenInfo:data,
timestamp:new Date().getTime()
}
} catch(err) {
console.log("err->", err.response.data)
return res.status(500).send({ret_code: ReturnCodes.SOMETHING_WENT_WRONG});
}
}
My previous code pass the data like this, which is wrong
async function refreshToken(){
try{
let headers = {
authorization: '',
'Content-Type': ''
}
let url = ``
let body = {
grant_type: '',
refresh_token: global.tokenInfo.refresh_token
}
data = await axios.post(url, body, {headers});
let data = response.data
console.log(data)
return {
tokenInfo:data,
timestamp:new Date().getTime()
}
} catch(err) {
console.log("err->", err.response)
return res.status(500).send({ret_code: ReturnCodes.SOMETHING_WENT_WRONG});
}
}
Simply try my first code, hope that solves your issue.
Most of the time it happens because of using wrong content type header.
Open postman and see "Body" tab. There you can find the content type of your post data. It's also accessible from "Headers" tab. There should be a Content-Type header. The correct format of data you send through a POST request depends on Content-Type header. for example, json content type requires a json (javascript object) data or form-data content type requires a FormData.
To set a header in axios, change the code like this:
axios.post("http://127.0.0.1:5000/login", {
username: this.state.username,
password: this.state.password
}, {
headers: {'Content-Type': 'application/json'}
}).then(function(response) {
console.log(response);
}).catch(function(error) {
console.log(error);
})
I had similar error i had the JSON capital and it should have been lowercase