POST Request Body is empty - mysql

Task
Parse a CSV file
Send the data to an API enpoint
Save data to MySql database
Problem
The request body is showing up empty when I send data via fetch. However, I can send and see the body data if I use Postman.
I've added a console.log(req.body) and it's printing out {} to the console.
Parse and Send Data to Endpoint
const changeHandler = (event) => {
Papa.parse(event.target.files[0], {
header: true,
skipEmptyLines: true,
complete: function (results) {
results.data.forEach(entry => {
// Create the data object.
let data = {};
let keys = ['Date', 'Description', 'Debit Amount'];
for (let key in entry) {
if (keys.includes(key)) {
data[key.toLowerCase().replaceAll(' ', '_')] = entry[key];
}
}
// Send data to server
fetch('http://localhost:3001/api/create_transactions', {
method: 'POST',
mode: 'no-cors',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
}).then(function (response) {
console.log(response);
})
});
},
});
// Reset file input
event.target.value = null;
};
Save Data to MySql
app.use(express.json());
const crypto = require('crypto');
app.post("/api/create_transactions", (req, res) => {
console.log(req.body);
/*
let hash = crypto.createHash('md5').update(req.body['date'] + req.body['description'] + req.body['debit_amount']).digest('hex');
let data = [
hash,
req.body['date'],
req.body['description'],
req.body['debit_amount'],
];
db.query('insert into transactions (`hash`, `date`, `description`, `debit_amount`) values (?, ?, ?, ?)', data, (err, result, fields) => {
if (err) {
console.log(err);
} else {
console.log(result);
res.send(JSON.stringify({"status": 200, "error": null, "response": result}))
}
});
*/
});
app.listen(PORT, () => {
console.log(`Server listening on ${PORT}`);
});

According to this post Fetch: post json data, application/json change to text/plain you can not change the Content-Type to application/json if you are using no-cors. So I will have to enable cors if I want to use fetch.
Using this tutorial https://www.section.io/engineering-education/how-to-use-cors-in-nodejs-with-express/ I was able to enable cors on my nodejs server and receive the proper headers.

Try to use express's bodyParser app.use(express.bodyParser());

Related

Ajax & Node JS : Paramaters values are NULL in backend

I'm working on a web app and I'm sending a post request with ajax to a node+express backend. The problem is that in the backend the values for all parameters are NULL, I have checked by console.log(data) on the front end before sending ajax request and I'm getting the values here but on the backend request.query has all params with NULL values.
AJAX Request
const data = {
first_name: fn,
last_name: ln,
email: email,
password: password,
job_title: job,
security: security,
mobile: mobile,
remarks: remarks,
};
console.log("Data : ");
console.log(data);
$.post(
"http://127.0.0.1:4000/user/add",
data,
function (response) {
console.log(response);
}
);
Console Log For Data
Data :
{first_name: 'a', last_name: 'a', email: 'admin#gmail.com', password: '13011301', job_title: 'CV-Specialist', …}
Backend Code
app.post("/user/add", (req, res) => {
const data = req.query;
var sql =
"Insert into users (first_name,last_name,email,password,job,security,mobile,remarks) values (?,?,?,?,?,?,?,?)";
conn.query(
sql,
[
data.first_name,
data.last_name,
data.email,
data.password,
data.job,
data.security,
data.mobile,
data.remarks,
],
function (err, result) {
if (err) {
res.send(err);
} else {
res.send("1 record inserted");
}
}
);
});
Backend Response
{code: 'ER_BAD_NULL_ERROR', errno: 1048, sqlMessage: "Column 'first_name' cannot be null", sqlState: '23000', index: 0, …}
code
:
"ER_BAD_NULL_ERROR"
errno
:
1048
index
:
0
sql
:
"Insert into users (first_name,last_name,email,password,job,security,mobile,remarks) values (NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL)"
sqlMessage
:
"Column 'first_name' cannot be null"
I have searched for solutions and explanations but I can't figure out what's causing this. Any help or hints will be appreciated, thank!
The default content type header of jquery for Ajax is application/x-www-form-urlencoded. You are expected to send data in the query Params like so:
$.ajax({
url: 'http://www.example.com?' + $.param({first_name: 'a', last_name: 'a', email: 'admin#gmail.com', password: '13011301', job_title: 'CV-Specialist', …}),
method: 'POST'
});
Either send all of your data encoded in query params as shown above
or
set headers to application/json
$.ajax({
url: 'YourRestEndPoint',
headers: {
'Content-Type':'application/json'
},
method: 'POST',
data: data,
success: function(data){
console.log('succes: '+data);
}
});
on the server side ensure you have body parser configured:
const bodyParser = require('body-parser');
.
.
.
.
.
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
In your middleware callback get data in req.body instead of req.query :
app.post("/user/add", (req, res) => {
const data = req.body;
var sql =
"Insert into users (first_name,last_name,email,password,job,security,mobile,remarks) values (?,?,?,?,?,?,?,?)";
conn.query(
sql,
[
data.first_name,
data.last_name,
data.email,
data.password,
data.job,
data.security,
data.mobile,
data.remarks,
],
function (err, result) {
if (err) {
res.send(err);
} else {
res.send("1 record inserted");
}
}
);
});

API returns index.ejs HTML template instead of JSON

When trying to bring a news/posts list from a common WordPress RSS Feed (Rest API), it ends up returning my index.ejs webpack template in my app instead of the JSON response.
Using an endpoint like: example.com/wp-json/wp/v2/posts, the expected response would be a JSON "string" with a certain number of posts,
and instead it's returning a text/html response, which happens to be the html template for my app, nothing unkown.
The code in JS, React being used is:
import axios from 'axios';
const getPosts = async () => {
const postsEndpoint = '/wp-json/wp/v2/posts';
const config = {
headers: {
'accept': 'text/html, application/json',
'Content-Type': 'application/json; charset=UTF-8',
},
params: {
_limit: 5,
}
};
let response;
try {
response = await axios.get(postsEndpoint, config);
} catch (err) {
console.log(err);
} finally {
// eslint-disable-next-line no-unsafe-finally
return response.json;
}
};
export default getPosts;
And the code to show this in display:
const blogNews = () => {
const [isLoading, setIsLoading] = useState(false);
const [posts, setPosts] = useState(null);
const getPostsData = async () => {
try {
setIsLoading(true);
const data = await getPosts();
console.log(data);
if (data.status === 200) {
setPosts(data);
setIsLoading(false);
}
} catch (err) {
console.log(err);
} finally {
setIsLoading(false);
}
};
useEffect(() => {
getPostsData();
}, []);
return ...
And the nginx configuration lines looks like this:
location /wp-json/wp/v2/ {
proxy_pass http://example.com/;
}
I'm using 'text/html' in the Accept header just to see what's in the response, if I only include 'application/json' it just returns a 404 error.
The fact that I'm not including Cors nor CSP headers it's because it's because the policy criteria is being met with the nginx configuration.
Do you guys have any recommendations regarding this? Thanks in advance.

AWS Lambda Node.js Environment Failed to Loop Insert to Mysql on First API Call

I'm trying to create function in AWS Lambda (node.js), which call some REST API, dan insert the API result to MySQL DB.
While the requirement is very simple, but I encounter some problem when deploying to AWS Lambda (not happening on my local machine), where my first API call only resulting only 1 data is inserted, while the second API call forward, it insert all 4 data as intended. I try various solution available on stack overflow, and all resulting the same.
Another problem is that the result is always {"message": "Internal server error"}, even though the data is inserted correctly on second API call forwards
Basically i don't have much experience with Node.js, so i would appreciate if anyone could help me.
'use strict';
const connection = require('serverless-mysql')({
config: {
host: 'xxxxxx.xxxxx.ap-southeast-1.rds.amazonaws.com',
user: 'xxx',
password: 'xxx',
database: 'xxx_db'
}
})
const axios = require('axios');
exports.handler = (event, context) => {
//Get Data From API
axios.get('https://xxx.xyz/wp-json/wp/v2/posts')
.then(res => {
const headerDate = res.headers && res.headers.date ? res.headers.date : 'no response date';
console.log('Status Code:', res.status);
console.log('Date in Response header:', headerDate);
//this should result 4 data
const posts = res.data;
posts.forEach(post => {
var sql = `INSERT INTO tbl_post(news_id, title, excerpt, content, category, image_link, modified_date, show_in_banner_F, show_in_list_F) VALUES ('${post.id}', '${post.title.rendered}', '${post.excerpt.rendered}', '${post.content.rendered}', '', '${post.yoast_head_json.og_image[0].url}', now(), 0, 0)`;
console.log(sql);
let insert_query = connection.query(sql);
});
console.log("finished");
connection.end();
let responseBody = { message: "OK" };
let response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers" : "Content-Type",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET"
},
body: JSON.stringify(responseBody)
};
return response;
})
.catch(err => {
console.log('Error: ', err.message);
let responseBody = { message: "Fail" };
let response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers" : "Content-Type",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET"
},
body: JSON.stringify(responseBody)
};
return response;
});
}
First of all a forEach loop will call connection.query(sql) function multiple times then exit the loop without actually waiting for each query to finish executing so you'll end up executing random number of queries each time you run this loop instead what you want to do is use async/await await connection.query(sql) in order to wait for each query inside the loop to finish executing before exiting the loop.
Also forEach loop is not designed for asynchronous code so you'll have to change that as well and use for...of instead. And you also have to use prepared statements using ? instead of inserting values with ${variable} to prevent sql injections.
for (const post of posts) {
const sql = "INSERT INTO tbl_post(news_id, title) VALUES (?, ?)";
const values = [post.id, post.title.rendered];
console.log(mysql.format(sql,values)); // This would log query after values substitution
await connection.execute(sql, values);
}
So the final code will look something like this:
exports.handler = async (event, context) => {
try {
//Get Data From API
const res = await axios.get("https://xxx.xyz/wp-json/wp/v2/posts");
const headerDate = res.headers && res.headers.date ? res.headers.date : "no response date";
console.log("Status Code:", res.status);
console.log("Date in Response header:", headerDate);
//this should result 4 data
const posts = res.data;
for (const post of posts) {
const sql = "INSERT INTO tbl_post(news_id, title) VALUES (?, ?)";
const values = [post.id, post.title.rendered];
console.log(mysql.format(sql, values)); // This would log query after values substitution
await connection.execute(sql, values); // Execute prepares statement first then executes it.
}
console.log("finished");
await connection.end();
let responseBody = { message: "OK" };
let response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers": "Content-Type",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET",
},
body: JSON.stringify(responseBody),
};
return response;
} catch (err) {
console.log("Error: ", err.message);
let responseBody = { message: "Fail" };
let response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers": "Content-Type",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET",
},
body: JSON.stringify(responseBody),
};
return response;
}
};
As a side note consider using transactions if u want to guarantee that all queries inside the loop either succeed or fail.
Pro tip: use Promise.all() if u want to execute multiple async functions at the same time not one after the other.

Why is my Response of Type Vague on this google cloud function?

I am calling a Google Cloud Function. I have a green arrow beside the function. I have enabled it to use HTTP. I have enabled it to work without authorization.
When I call it I get an error 403 and an object that says response type opaque. I have no idea what is wrong now ...
I have spent an entire day on this. And I'm so confused.
I'm calling it from both localhost:3000, and from an app I built on https://djit.su/
I have tried my best to figure out the CORS and everything else, but I am so stuck.
At this point I just want it to return "hey" to my local machine...
Here is my Google Cloud FN:
'use strict'
const functions = require('firebase-functions');
const cors = require('cors')({
origin: true,
});
exports.date = functions.https.onRequest((req, res) => {
console.log('Made it to here!');
if (req.method === 'PUT') {
return res.status(403).send('Forbidden!');
}
return cors(req, res, () => {
const stringVar = 'This is a string var';
console.log('Hi from inside cloud fn');
console.log('Sending:', { name: "Hi I'm Rick", stringVavr: stringVar });
const options = {
secure: false,
signed: false,
sameSite: None,
};
res.cookie('session', 'ABCE', options);
res.set('Access-Control-Allow-Methods', 'GET');
res.set('Access-Control-Allow-Headers', 'Content-Type');
res.set('Access-Control-Max-Age', '3600');
res.header('Access-Control-Allow-Origin', '*');
res.set('Access-Control-Allow-Origin', '*');
if (req.method === 'OPTIONS') {
res.set('Access-Control-Allow-Methods', 'GET');
res.set('Access-Control-Allow-Headers', 'Content-Type');
res.set('Access-Control-Max-Age', '3600');
res.status(204).send('');
}
});
});
Here is my React code:
async function getNum() {
await fetch(
'https://us-central1-provable-fair-algo-rick.cloudfunctions.net/randomHash',
{ mode: 'no-cors' }
)
.then(function (response) {
// return response.text();
console.log(response);
console.log(json.response);
Promise.resolve(response);
console.log(Promise.resolve(response));
})
.then(function (text) {
console.log('Request successful', text);
})
.catch(function (error) {
console.log('Request failed', error);
});
Here is the console.log
Response {type: "opaque", url: "", redirected: false, status: 0, ok: false, …}
body: (...)
bodyUsed: false
headers: Headers {}
ok: false
redirected: false
status: 0
statusText: ""
type: "opaque"
url: ""
The issue seems to be on your react code, as you are using { mode: 'no-cors' } however the Cloud Function is using CORS as it is in a different domain.
Here it explains that using no-cors can generate this opaque response:
no-cors is intended to make requests to other origins that do not have CORS headers and result in an opaque response, but as stated, this isn't possible in the window global scope at the moment.
A way to correct this would be to use: { mode: 'cors' } The code will be as the following:
function getNum() {
await fetch(
'https://us-central1-provable-fair-algo-rick.cloudfunctions.net/randomHash',
{ mode: 'cors' }
)
.then(function (response) {
// return response.text();
console.log(response);
console.log(json.response);
Promise.resolve(response);
console.log(Promise.resolve(response));
})
.then(function (text) {
console.log('Request successful', text);
})
.catch(function (error) {
console.log('Request failed', error);
});

nodeJS - make HTTPS request, sending JSON data

I would like to send an HTTPS POST from one nodeJS server to another. I have some JSON data I would like to send with this request (populated by a html form).
How can I do this? I am aware of https.request() but there does not seem to be an option to include JSON as a part of the query. From my research it seems possible with an HTTP request, but not an HTTPS request. How can I solve this?
const pug = require('pug');
var cloudinary = require('cloudinary');
var express = require('express');
var multer = require('multer');
var upload = multer({ dest: 'uploads/' });
var request = require('request');
var bodyParser = require('body-parser');
var options = {
hostname: 'ec2-54-202-139-197.us-west-2.compute.amazonaws.com',
port: 443,
path: '/',
method: 'GET'
};
var app = express();
var parser = bodyParser.raw();
app.use(parser);
app.set('view engine', 'pug');
app.get('/', upload.single('avatar'), function(req, res) {
return res.render('index.pug');
});
app.get('/makeRequest*', function(req, res) {
query = req['query'];
/*
Here, I would like to send the contents of the query variable as JSON to the server specified in options.
*/
});
You can send JSON data through a POST http request with the native https node module, as stated in the documentation
All options from http.request() are valid.
So, taking the http.request() example you can do the following:
var postData = querystring.stringify({
'msg' : 'Hello World!'
});
var options = {
hostname: 'www.google.com',
port: 80,
path: '/upload',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(postData)
}
};
var req = https.request(options, (res) => {
console.log(`STATUS: ${res.statusCode}`);
console.log(`HEADERS: ${JSON.stringify(res.headers)}`);
res.setEncoding('utf8');
res.on('data', (chunk) => {
console.log(`BODY: ${chunk}`);
});
res.on('end', () => {
console.log('No more data in response.');
});
});
req.on('error', (e) => {
console.log(`problem with request: ${e.message}`);
});
// write data to request body
req.write(postData);
req.end();
You should edit postData to your desired JSON object
I believe the below is what you want. Using the request library. See comments in the code for my recommendations.
...
var options = {
hostname: 'ec2-54-202-139-197.us-west-2.compute.amazonaws.com',
port: 443,
path: '/',
method: 'POST',
json: true
};
...
//making a post request and sending up your query is better then putting it in the query string
app.post('/makeRequest', function(req, res) {
var query = req.body['query'];
//NOTE, you cannot use a GET request to send JSON. You'll need to use a POST request.
//(you may need to make changes on your other servers)
options.body = { payload: query };
request(options, function(err, response, body) {
if (err) {
//Handle error
return;
}
if (response.statusCode == 200) {
console.log('contents received');
}
});
});
as matt mentioned you need to use request
to send JSON object not JSON.Stringify so that at the server you can receive it using:
app.post('/makeRequest', function(req, res) {
console.log (req.body.param1);
}
Use the following code:
var request = require("request");
request({
'url':"http://www.url.com",
method: "POST",
json: true,
body: {'param1':'any value'}
}, function (error, resp, body) {
console.log ("check response.");
});