req.body.something returns undefined - reactjs

I have been trying to post data to my express server using axios, and when I console.log(req.body.something) it returns undefined, and when I console.log(req.body) only it logs this message to the console:
[Object: null prototype] { '{"nameVal":"Usef","nickNameVal":"US"}': '' }
Any Help Will Be Appreciated.
// This My Server.js Code
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const app = express();
app.use(bodyParser.json());
// create application/x-www-form-urlencoded parser
const urlencodedparser = bodyParser.urlencoded({ extended: false });
// Use Cors As MiddleWhere
app.use(cors());
// Get The Post Request
app.post("/user", urlencodedparser, (req, res) => {
console.log(req.body.name); // returns undefined
});
app.listen(5000);
// and this the react component state along with the axios post request
state = {
nameVal: "Usef",
nickNameVal: "US"
};
handleSubmit = event => {
event.preventDefault();
const { nameVal, nickNameVal } = this.state;
axios.post("http://localhost:5000/user", { nameVal, nickNameVal },
{ headers: { "Content-Type": "application/x-www-form-urlencoded" } }
).then(res => {console.log(res)});
};

If you remove your custom Content-Type header from the axios request, axios will send your data as JSON by default, and it will be parsed by your express JSON parser middleware.
axios.post("http://localhost:5000/user", { nameVal, nickNameVal })
.then(res => console.log(res));
The data you send to the server is nameVal and nickNameVal, so trying to access req.body.name will still give undefined. Try logging nameVal and nickNameVal instead.
app.post("/user", (req, res) => {
console.log(req.body.nameVal, req.body.nickNameVal);
});

According to axios documentation you need to pass an instance of URLSearchParams (or a query string of the parameters) as the second argument.
const params = new URLSearchParams();
params.append('nameVal', state.nameVal);
params.append('nickNameVal', state.nickNameVal);
axios.post('/user', params);

Related

Why express server receives front end data as undefined?

I am currently working on social media mern stack react app. I am using node js and express as my backend services , also using mongoose to store my data and axios and redux thunk which connect the backend to the front end. Till now I had no issue recieving and sending data to the server. Right now I am trying to create search post get request ,base on a keyword the user entered. The issue with it, that when I am sending the keyword to the server instead of recieving the string it gets undefined value, like redux thunk not sending anything. I will be very thankful if someone could help me with that. I am watching the code over and over again and can't find out the reason for that.
My post controller class(I copied only the relevant function):
import express from "express";
const app = express();
import Post from "../model/PostModel.js";
import ErrorHandlng from "../utilities/ErrorHandling.js";
import bodyParser from "body-parser";
import catchAsync from "../utilities/CatchAsync.js";
import User from "../model/UserModel.js";
app.use(express.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
export const getPosts = catchAsync(async (req, res, next) => {
const data = req.body.keyword;
const page = parseInt(req.query.page || "0");
const PAGE_SIZE = 20;
const query = new RegExp(data, "i");
const total = await Post.countDocuments({});
const posts = await Post.find({ $or: [{ title: query }, { content: query }] })
.limit(PAGE_SIZE)
.skip(PAGE_SIZE * page);
if (!posts) {
return next(new ErrorHandlng("No posts were found", 400));
}
res.status(200).json({
status: "success",
data: {
totalPages: Math.ceil(total / PAGE_SIZE),
posts,
},
});
});
My api class(front end,copied only the calling for that specific get request):
import axios from "axios";
const baseURL = "http://localhost:8000";
axios.defaults.withCredentials = true;
const API = axios.create({
baseURL,
credentials: "include",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
});
export const getPostsByKeyword = (keyword, page) =>
API.get(`/post/getPostsByKey?page=${page}`, keyword);
Post slice class:
export const fetchPostsByKeyWord = createAsyncThunk(
"post/getKeyword",
async ({ keyword, page }, { fulfillWithValue, rejectWithValue }) => {
try {
const response = await api.getPostsByKeyword(keyword, page);
if (response.statusCode === "400") {
throw new Error("There are no available posts");
}
const fetchData = await response.data.data.posts;
const totalPages = await response.data.data.totalPages;
return fulfillWithValue({ fetchData, totalPages });
} catch (err) {
console.log(err.response.message);
}
}
);
const initialState = { status: "undefined" };
const PostSlice = createSlice({
name: "post",
initialState,
reducers: {},
extraReducers: {},
});
export const postActions = PostSlice.actions;
export default PostSlice;
Calling the backend:
dispatch(fetchPostsByKeyWord({ keyword, page }))
.unwrap()
.then((originalPromiseResults) => {
console.log("thte " + " " + originalPromiseResults.totalPages);
console.log("The data is" + originalPromiseResults.fetchData);
setTotalPages(originalPromiseResults.totalPages);
})
.catch((err) => {
console.log(err.message);
});
As you can see I have not copied the whole code, I copied only the parts that are relevants for the question.
Browsers cannot currently send GET requests with a request body. XMLHttpRequest (which Axios uses) will ignore it and fetch() will trigger an error.
See also HTTP GET with request body for extra discussion on why trying this might be a bad idea.
You should instead pass everything required in the query string, preferably via the params option so it is correctly encoded...
export const getPostsByKeyword = (keyword, page) =>
API.get("/post/getPostsByKey", { params: { page, keyword } });
and grab the data via req.query server-side.
const { page, keyword } = req.query;
With vanilla JS, you can use URLSearchParams to construct the query string...
const params = new URLSearchParams({ page, keyword });
// XHR
const xhr = new XMLHttpRequest();
xhr.open("GET", `/post/getPostsByKey?${params}`);
// Fetch
fetch(`/post/getPostsByKey?${params}`); // GET is the default method
Your Axios instance creation could also be a lot simpler...
Axios is usually quite good at setting the correct content-type header, you don't have to
Your Express app isn't doing any content-negotiation so you don't need to set the accept header
Unless you're actually using cookies (which it doesn't look like), you don't need credential support
const API = axios.create({ baseURL });

sending token to backend server but couldn't connect to it with react-google-recaptcha

I'm trying to send token to my backend server to let it verify the token with Google recaptcha. However, my frontend does not seem to connect with my backend.
Error Message I'm getting: POST http://localhost:3000/post 404 (Not Found)
Apparently, my FE could not find the port to BE.
I have tried to use frontend for API request with the URI provided by google but I ran into CORS issue, and it is not secure.
By console.log(token), I find nothing wrong with the library itself because I have successfully get a success response from Google using the token with POSTMAN.
Here is my frontend:
const handleVerify = async (token) => {
console.log(token)
const captchaToken = token;
await axios.post("/post", { captchaToken })
.then(res => console.log(res)
.catch((error) => {
console.log(error)
}))
setIsVerified(true)
setActiveStep(activeStep + 1);
}
.....
<ReCAPTCHA
ref={recaptchaRef}
sitekey={process.env.REACT_APP_SITE_KEY}
onChange={(token) => handleVerify(token)}
size="normal"
onExpired={(expire) => handleShowExpired(expire)}
>
</ReCAPTCHA>
.....
Here is my backend:
const express = require("express");
const cors = require("cors");
const axios = require("axios");
const app = express();
const port = process.env.PORT || 5000;
app.use(cors());
app.use(express.urlencoded({ extended: true }));
app.use("/", router);
//POST route
router.post("/post", async (req, res) => {
const { captchaToken } = req.body;
//sends secret key and response token to google
await axios.post(
`https://www.google.com/recaptcha/api/siteverify?secret=${process.env.SECRET_KEY}&response=${captchaToken}`
);
//check response status and send back to client
if (res.data.success) {
res.send("Human");
} else {
res.send("Robot");
}
});
If your server runs on port 5000 you should await axios.post("http://localhost:5000/post", { captchaToken })
Also, you have to validate the axios response.
router.post("/post", async (req, res) => {
const { captchaToken } = req.body;
//sends secret key and response token to google
const axiosResponse = await axios.post(
`https://www.google.com/recaptcha/api/siteverify?secret=${process.env.SECRET_KEY}&response=${captchaToken}`
);
//check response status and send back to client
if (axiosResponse.data.success) {
res.send("Human");
} else {
res.send("Robot");
}
});

How to return a promise from Lambda

I'm trying to connect my NextJS application via AWS Amplify to the Google Calendar API. I added an API with "Amplify Add API". Selected "Rest" and chose "Serverless Express Function". So far so good. The test function works. I am utilizing Proxy integration to let lambda decide what to do with the code. Here is my client side code:
import React from "react";
import { API } from "aws-amplify";
const getCalendar = () => {
React.useEffect(() => {
getCal();
async function getCal() {
const apiName = 'gcal'; // api name.
const path = '/events'; // API path
await API
.get(apiName, path)
.then(response => {
console.log(response)
}).then(() => console.log(` the resopnse returned
successfully as: ${JSON.stringify(response.body)
}`))
.catch(error => {
console.log(`there was an error returning the
response from the lambda function: ${error}`);
});
}
},[])
}
export default getCalendar;
Inside the Lambda function I started with the Proxy Integration sample and replaced with the code below. The issue is, the response is visible and correct from Google API but not received by the client from Lambda. I tried a callback, async/await, and res.send(). I know I'm missing something in this function below regarding the async nature of the request. Getting 500 or 502 errors from Cloud Watch Logs. Lambda index.js code(with problem):
const awsServerlessExpress = require("aws-serverless-express");
require("dotenv").config();
var express = require("express");
var bodyParser = require("body-parser");
var awsServerlessExpressMiddleware = require("aws-serverless-
express/middleware");
const { google } = require("googleapis");
const calendar = google.calendar("v3");
var app = express();
const server = awsServerlessExpress.createServer(app, null);
app.use(bodyParser.json());
app.use(awsServerlessExpressMiddleware.eventContext());
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "*");
res.header("Content-Type", "*");
next();
});
exports.handler = (event, context) => {
return awsServerlessExpress.proxy(server, event, context,
"PROMISE").promise;
};
app.post("/events", function () {
main();
async function main() {
const auth = new google.auth.GoogleAuth({
keyFilename: "./<my-secret-file>.json",
scopes: [
"https://www.googleapis.com/auth/calendar",
"https://www.googleapis.com/auth/calendar.readonly",
],
});
const now = new Date().toISOString();
// Acquire an auth client, and bind it to all future calls
const authClient = await auth.getClient();
google.options({ auth: authClient });
// Do the magic
let response = await calendar.events.list({
calendarId: process.env.API_CALENDAR_ID,
showHiddenInvitations: false,
timeMin: now,
showDeleted: false,
});
return await response.json();
}
return res.send(response);
});
app.listen(3000, function () {
console.log("Google Calendar Function Is Running");
});
Here is the working response from Google API to Lambda in Dev Tools:
I am receiving {"message": "Internal server error"} after completion.
Here is the failed attempted response to the client from Lambda:
inside Cloud Watch Logs I get "no help", and a succeeded response:
Any help or suggestions would be greatly appreciated.
I found out the answer was in both mixing promises with async await and also with res.send(). After returning the data from Google API I still needed to format the response and set the correct headers. Here is the working code.
const awsServerlessExpress = require("aws-serverless-express");
require("dotenv").config();
var express = require("express");
var bodyParser = require("body-parser");
var awsServerlessExpressMiddleware = require("aws-serverless-
express/middleware");
var app = express();
const server = awsServerlessExpress.createServer(app, null);
app.use(awsServerlessExpressMiddleware.eventContext());
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "*");
res.header("Content-Type", "application/json");
next();
});
app.use(bodyParser.json());
const { google } = require("googleapis");
const calendar = google.calendar("v3");
exports.handler = (event, context) => {
return awsServerlessExpress.proxy(server, event, context,
"PROMISE").promise;
};
app.get("/events", function( req, res ) {
async function main() {
try {
const auth = new google.auth.GoogleAuth({
keyFilename: "./my-secret-file.json",
// Scopes can be specified either as an array or as a single,
space-delimited string.
scopes: [
"https://www.googleapis.com/auth/calendar",
"https://www.googleapis.com/auth/calendar.readonly",
],
});
const now = new Date().toISOString();
// Acquire an auth client, and bind it to all future calls
const authClient = await auth.getClient();
google.options({ auth: authClient });
let responseBody = await calendar.events.list({
calendarId: process.env.API_CALENDAR_ID,
showHiddenInvitations: false,
timeMin: now,
showDeleted: false,
});
// Do the magic
let response =
{
"isBase64Encoded": "false",
"statusCode": "200",
"headers": {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "*",
"Content-Type": "application/json",
"Access-Control-Allow-Methods": "*"
},
"body": responseBody,
};
return res.send(JSON.stringify(response))
} catch (error) {
console.log(error);
}}
main();
});
app.listen(3000, function () {
console.log("The Google Calendar Function Is Running");
});

Express csurf middleware always accepted even if i don't attach _csrf in the req.body from react app but api works in postman as expected

Hi i am trying to acieve CSRF protection in react js and express js application. The express api is working properly with postman, when i attach _csrf : token with the req.body then it works otherwise it throw invalid csrf token. thats perfect.But when i call the api from the react app using axios its working without the csrf token ,please help. thanks
Here is the code for express:
const express = require('express')
const cookieParser = require('cookie-parser')
const csurf = require('csurf')
const cors = require('cors')
const app = express();
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
app.use(cors({ origin: true, credentials: true }));
app.use(cookieParser());
app.use(csurf({
cookie: {
httpOnly: true,
maxAge: 30//30 seconds
}
}));
app.use((req, res, next) => {
res.cookie('XSRF-TOKEN', req.csrfToken());
next();
});
//do some operation (this should be csrf protected )
//its protected in postman but not in my react app
app.post('/post', authMid, (req, res) => {
console.log("req.headers - ", req.headers['x-xsrf-token']);
res.json({ name: "some operation happen" })
});
My React Code:
import axios from 'axios'
import { useState } from 'react'
axios.defaults.baseURL = 'http://localhost:2727/' //api base url
axios.defaults.withCredentials = true
//react App componet
function App() {
const [msg, setMsg] = useState("")
const doOperation= async () => {
try {
//i don't attach the XSRF-TOKEN but still the request is successfull
//but its not successfull if the XSRF-TOKEN is not attach in post man.
const res = await axios.post('/post')
console.log(res.data);
setMsg(res.data)
} catch (e) {
setM(e.message)
}
}
return (
<div>
<button onClick={doOperation}>do Operation</button>
<h1>{msg}</h1>
</div>
);
}
export default App;
X-XSRF-TOKEN is automatically set and sent in the request headers by Axios in my react app that's why when I did not set the _csrf token it still works. But in postman, it doesn't work that way that's why I need to add the _csrf token manually in the body.

How to Get Param Id from URL in express/mongo/mongoose on server side, axios/react/redux on client side

I am having some trouble with get the param from the url. I use Express(4.16.3) on the server side, and using Axios to make the request. But I couldn't seem to get the param from the url in Express.
Here is my code:
on my Route.js in Express
app.get('/api/surveys/:surveyId', (req, res, next) => {
var id = req.params.surveyId;
console.log(req.params);
// it gets params {surveyId: ':surverId'}
res.send('Hello World');
});
so instead of getting the actual id, it logs params: {surveyId: ':surveyId'}. I have been researching, but seems this is the correct way to do it. I also use axios to make the request:
in actions/index.js (I use react):
export const fetchOneSurvey = () => async dispatch => {
const res = await axios.get('/api/surveys/:surveyId');
dispatch({ type: FETCH_ONE_SURVEY, payload: res.data });};
Not sure if this is relevant:
On the view page, instead of having http://localhost:3000/api/surveys/:surveyId, I have http://localhost:3000/surveys/:surveyId route set in React. When I go to http://localhost:3000/surveys/:surveyId, it does console log (req.params) like I write in express, but I only get a string ':surveyId' is the params, not the actual id on the url.
Please anyone can help me? I have tried many different ways, but nothing seem working. I thank you all very much in advance.
===== Extra section ======
Here is my index.js:
const express = require('express');
const mongoose = require('mongoose');
const cookieSession = require('cookie-session');
const passport = require('passport');
const bodyParser = require('body-parser');
const keys = require('./config/keys');
require('./models/User');
require('./models/Survey');
require('./services/passport');
mongoose.connect(keys.mongoURI);
const app = express();
app.use(bodyParser.json());
app.use(
cookieSession({
maxAge: 30 * 24 * 60 * 60 * 1000,
keys: [keys.cookieKey]
})
);
app.use(passport.initialize());
app.use(passport.session());
require('./routes/authRoutes')(app);
require('./routes/billingRoutes')(app);
require('./routes/surveyRoutes')(app);
if (process.env.NODE_ENV === 'production') {
app.use(express.static('client/build'));
const path = require('path');
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
});
}
My survey model route js:
const _ = require('lodash');
const Path = require('path-parser');
const { URL } = require('url');
const mongoose = require('mongoose');
const requireLogin = require('../middlewares/requireLogin');
const requireCredits = require('../middlewares/requireCredits');
const Mailer = require('../services/Mailer');
const surveyTemplate = require('../services/emailTemplates/surveyTemplate');
const Survey = mongoose.model('surveys');
module.exports = app => {
app.get('/api/surveys', requireLogin, async (req, res) => {
const surveys = await Survey.find({ _user: req.user.id }).select({
recipients: false
});
res.send(surveys);
});
app.get('/api/surveys/:surveyId/:choice', (req, res) => {
res.send('thanks for voting');
});
app.get('/api/surveys/:surveyId', (req, res, next) => {
var id = req.params.surveyId;
console.log(id);
// it gets params {surveyId: ':surverId'}
res.send('Hello World');
});
app.post('/api/surveys/webhooks', (req, res) => {
// console.log(req.body);
// res.send({});
const p = new Path('/api/surveys/:surveyId/:choice');
const test = _.chain(req.body)
.map(({ email, url }) => {
const match = p.test(new URL(url).pathname);
if (match) {
return {
email,
surveyId: match.surveyId,
choice: match.choice
};
}
})
.compact()
.uniqBy('email', 'surveyId')
.each(({ surveyId, email, choice }) => {
Survey.updateOne(
{
// have to add _ to keys as mongoDB rule, mongoose doensn't need.
_id: surveyId,
recipients: {
$elemMatch: { email: email, responded: false }
}
},
{
$inc: { [choice]: 1 },
$set: { 'recipients.$.responded': true },
lastResponded: new Date()
}
).exec();
})
.value();
console.log(test);
res.send({});
});
app.post('/api/surveys', requireLogin, requireCredits, async (req, res) => {
const { title, subject, body, recipients } = req.body;
const survey = new Survey({
// map(email => ({ email }) === map(email =>{ return {email: email}})
title,
body,
subject,
recipients: recipients
.split(',')
.map(email => ({ email: email.trim() })),
_user: req.user.id,
dateSent: Date.now()
});
// send an email
const mailer = new Mailer(survey, surveyTemplate(survey));
try {
await mailer.send();
await survey.save();
req.user.credits -= 1;
const user = await req.user.save();
res.send(user);
} catch (err) {
res.status(422).send(err);
}
});
};
Posting below details for debugging the issue
Note: if you are using Windows OS, use command prompt for node project development. i have seen people using git bash for doing node project developments and it causes unnecessary issues
Below are the steps for debugging
1.Create a new directoryforexample test and initialize it using npm init
2.Install express npm install --save express
3.Create a new file for example index.js and use below code
test/index.js
var express= require("express");
var app = express();
app.get("/api/surveys/:surveyId",(req,res,next)=>{
console.log(req.params.surveyId);
res.send('Hello World');
});
var server= app.listen(3000,()=>{
console.log("port started at ",server.address().port);
})
4.Start the program node index.js
5.Trigger http request from browser http://localhost:3000/api/surveys/llads . The value llads can be accessed using the path param surveyId in the route
6.if you can see the below output in node console then the program is working as it should. And this has to work as described here.
if above steps yields expected output then i don't see any problem in your route code.
Let me know your feedback.

Resources