I want to post from a react app to an expressjs api - node.js

I am learning react and expressjs for fun. I have a REST api in express and I have all the get methods working with my react app. Meaning I can call and use the get or get/i and it works. I am trying to do the post method now and I think im missing some small parts. I know this code is messy but its just a sandbox
Here is my express post method. It does post and write to mongo hard coded now I am trying to send it a request from react and use those values. the console.log all say undefined in the log.
request [object Object]
tony params [object Object]
Tony Name undefined
Tony Path /api/courses
//const tony = validateCourse(req.body);
const tony = req;
console.log("request " + tony);
console.log("tony params " + tony.params);
console.log("Tony Name " + tony.name);
console.log("Tony Path " + tony.path);
// this posts with hard coded values need to get values from the react post now
MongoClient.connect('mongodb://localhost:27017', function (err, client) {
if (err) throw err;
var db = client.db('tc-fant-maps');
db.collection('mapss').insertOne({name: 'Map020', description: 'This is the map of test', path:'http://d9amq8b4jzm6k.cloudfront.net/elrue001.png'}, function (findErr, result) {
if (findErr) throw findErr;
//console.log(result);
client.close();
res.send(result);
});
});
Here is my react code that does hit the api as the hard coded values actually show up in mongo. Meaning it hits my post and my hardcoded method executes. I just dont see the new values im passing in the console.log
function CreateMap() {
const [name, setName] = useState('')
const [description, setDescription] = useState('')
const [path, setPath] = useState('')
const submit = e => {
e.preventDefault()
fetch(`http://localhost:3001/api/courses`, {
method: 'POST',
body: JSON.stringify({ name, description, path }),
})
console.log("tony yimt")
}
return (
<form onSubmit={submit}>
<label htmlFor="name">Map Name</label>
<input
name="name"
value={name}
onChange={e => setName(e.target.value)}
/>
<br />
<label htmlFor="description">EMap Description</label>
<input
type="description"
name="description"
value={description}
onChange={e => setDescription(e.target.value)}
/>
<br />
<label htmlFor="path">Map Image Path</label>
<input
type="path"
name="path"
value={path}
onChange={e => setPath(e.target.value)}
/>
<br />
<button type="submit">Send it!</button>
</form>
)
}
export default CreateMap ```
When I look at the post in chrome I see
request payload
{"name":"fran","description":"k","path":"foj"}
which is what I entered on my web form (just typed in junk) and I get a status 200 back.
I either do not know how to get the body from the request and use it in express or I am sending the payload wrong from react.
sorry for the mess as I said its a sandbox but it does send a payload.
so short story is im trying to replace the hard coded
({name: 'Map020', description: 'This is the map of test', path:'http://d9amq8b4jzm6k.cloudfront.net/elrue001.png'}
with the values in my react form post

if you don't have the body parser package, you should get it and add it to your app
var app = express();
app.use(bodyparser.json());
Then you can get the json body
npm install body-parser

Your fetch call, and api need to be modified a bit. Your fetch should look more like this:
fetch(`http://localhost:3001/api/courses`, {
method: 'POST',
body: JSON.stringify({ name: name,
description: description,
path: path }),
})
Notice that I added the property names, then set their values.
And your api needs to access the body object of the request:
const tony = req;
console.log("request " + tony);
console.log("tony params " + tony.body.params);
console.log("Tony Name " + tony.body.name);
console.log("Tony Path " + tony.body.path);
Notice the body property being accessed within the request object.

So it turned out to be a 'cors' issue. I had no cors set on the react app and my limited experience with it just assumed everything running on local host was ok and the error was not clear to me.
app.use(cors({
origin: 'http://localhost:3002'
}));
Once I added this to express side and enabled cors on react it worked.
That is 6 hours of my life I wont get back but good to have worked through it.

Related

Fetch Api is unable to post things to the server

I had created a chatroom in my website and I had used the default action of a form to post message's data into the server and store it in MongoDB. But, know I am trying to make it live by using fetch instead as the data will go the server without refreshing the page simultaneously. But, after posting the data to the server as JSON, the server is unable to get the data of the post. I am also using the Body Parser to get the data in the req object, but, that too isn't working and it shows that the data is null. Can someone tell me how I can fix this issue?
My HTML:
<form id="form-container">
<input type="text" id="user-name-input" name="userName">
<input required type="text" id="message-input" name="message" placeholder="Send a message here!">
<button type="submit" id="submit-button">Submit</button>
</form>
My Client-Side Javascript:
submitButton.addEventListener('click', (e) => {
e.preventDefault();
var message = document.getElementById('message-input').value;
fetch('/dps/chat/request-message-insert', {
method: 'POST',
body: JSON.stringify({
userName: userName,
message: message
}),
headers: {
'Content-Type': "application/json; charset=UTF-8"
}
}).then(res => res.json()).then(data => console.log(data));
})
My server.js file:
app.post('/dps/chat/request-message-insert', urlencodedParser, (req, res) => {
console.log(req.body)
const userName = req.body.userName;
const message = req.body.message;
client.connect(async (err) => {
const collection = client.db("gradilo").collection("chat-messages");
await collection.insertOne({ text: message, userName: userName })
await client.close();
})
})
I will check your code your server-side are perfect but I think the problem is client-side are a few issues.
Check async await because I think responses are not perfect get your DOM through user and Message is perfect work when sending API through post then show a Problem.
I will provide the following link to help you
https://www.topcoder.com/thrive/articles/fetch-api-javascript-how-to-make-get-and-post-requests
check this link same as your solution
I fixed my problem with a simple line of code. Looks like the client-side code and the server code is all fine. We have also specified headers in the options of the fetch too. The problem was that the server didn't know in which format it could expect data from the client. Thus, adding the following code before the app.post() method will fix this issue.
app.use(express.json());
Then, you do not even need the npm body-parser dependency to parse the incoming data. The data will be added to the req.body object without any additional middleware or dependency.

Should I call api from node (server side) or in react (client side)?

I have tried to call api from a govt website to get the covid vaccine available in that pincode. I created a function with api call in a file named searchbypinapi.js and imported and used the function in Pindiv.jsx. (I am posting the Code of both the file)
searchbypinapi.js
import axios from 'axios';
const Searchbypinapi = (pincode) => {
var today = new Date();
var date = today.toJSON().slice(0, 10);
var nDate = date.slice(8, 10) + '-' + date.slice(5, 7) + '-' + date.slice(0, 4);
let pincodeconfig = {
method: 'get',
url: 'https://cdn-api.co-vin.in/api/v2/appointment/sessions/public/calendarByPin?pincode=' + pincode +'&date=' + nDate ,
headers: {
'accept': 'application/json',
'Accept-Language': 'hi_IN',
'User-Agent': 'covid19'
}
};
axios(pincodeconfig)
.then((response) => {
return(response.data);
})
.catch(error => {
return error;
});
};
export default Searchbypinapi;
Pindiv.jsx
import React, { useState } from 'react';
import Searchbypinapi from '../../api/searchbypinapi';
const Pindiv = () => {
const [input, setInput] = useState("");
const handleChange = (event) => {
setInput(event.target.value);
}
const handleClick = (event) => {
Searchbypinapi(input)
.then((data) => {
console.log(data);
});
}
return (
<div id="pincode" className="pinsearch d-flex justify-content-center">
<div class="example">
<input type="text" autoComplete="off" onChange={handleChange} placeholder="Search.." name="search" />
<button type="button" onClick={handleClick} ><i class="fa fa-search"></i></button>
</div>
</div>
)
}
export default Pindiv
When I called the api in react it is giving error :-
click here to see the error screenshot
But when I run the same api in nodejs, it console.log() the data successfully.
I was confused as it is not running in reactjs. Please suggest me any solution of this or I should create a server folder to create api request and than send it to client side with axios.
Note :- I have also used other api's like (https://api.covid19india.org/data.json) they are running perfectly, only this api is not running.
Your request is blocked because of CORS (cross-origin request sharing).
The 3th party server (co-vin.in in this case) determines the origins (other websites) than can access the server. Apparently your website is not allowed.
However, CORS is enforced by the browser. That is why your request does not work from the browser (your React app) but is does work from your server.
As a solution, you can proxy the request through your server, as you already suggested.

Axios post in React front end leading to error when posting to MailerLite

I have the following component:
const WaitingListComponent = () => {
const [email, setEmail] = useState('')
const onSubmit = (e) => {
e.preventDefault()
axios.post("/api/waiting-list/addToList", {
email: email
})
}
return (
<form className="waiting-list-component-container" onSubmit={onSubmit}>
<h4 className="waiting-list-component-heading">Join our waiting list to get early access</h4>
<p className="waiting-list-component-paragraph">Join our waiting list to get exclusive early access to our platform when we're ready to launch.</p>
<input className="waiting-list-component-input" name="email" type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="janedoe#email.com" />
<GeneralButton type="submit" text="Get access" />
</form>
)
}
This Axios request is getting posted via the following function:
const MailerLite = require('mailerlite-api-v2-node').default;
const mailerLite = MailerLite(process.env.MAILER_API);
module.exports = (req, res) => {
res.statusCode = 200;
res.setHeader("Content-Type", "application/json");
const email = req.body.email;
mailerLite.addSubscriberToGroup(process.env.MAILER_GROUP, email)
.then(() => {
console.log("Successfully sent new subscriber to MailerLite.");
res.send(JSON.stringify({ success: true }));
})
.catch((err) => {
console.log("There was an error.");
console.log(err);
res.send(JSON.stringify({ success: false }));
});
};
This is a post to a website called 'MailerLite'.
Their documentation is here: https://developers.mailerlite.com/docs/getting-started-with-mailerlite-api
And the package I'm using to post via node is here: https://www.npmjs.com/package/mailerlite-api-v2-node#addsubscribertogroupgroupid-subscriber
I'm attempting to use the 'addSubscriberToGroup' function to add a new subscriber to my group.
However, despite the Axios post successfully going through - as shown in the error message - there is an error being generated each time.
I don't want to post the full error because it's lengthy and it contains the API key, but the final two lines indicate it's an Axios error:
isAxiosError: true
Can anyone point out where I'm going wrong here?
If you need more info, or have any specific questions, please let me know!
The issue is probably that you need to send email as an object. You could do it like this: addSubscriberToGroup('id', { email: email })

POST request to 3rd party URL from Angular/NodeJS/ExpressJS

Option 4.2 seems like the best direction for me. Does anyone have any other suggestions?
Is there a way to access response in any of the below scenarios or I need to rewrite whole logic?
I need to perform a form POST to a 3rd party payment provider with Angular Typescript with or without NodeJS/ExpressJS with the redirect.
Flow:
The problem is that in some cases when I perform URL redirect successfully I don't receive any response from payment gateway. When a user clicks "Pay" - "Plati" he is redirected to the success page http://example.com/success and in case of error response to page http://example.com/cancel.
The expected scenario
The user comes to the website selects the products and clicks on the buy button. At that point, s/he is taken to another page where s/he makes the payment. After successful payment, the user is redirected back to the website and I get a response from the server and show the user a related message.
Option 1 - Form Action URL
If I do standard form submit and put payment gateway URL inside [action]="'https://test-wallet.example.com/checkout/'" then the user will be redirected directly to that URL and payment will be processed successfully. But in that case I don't receive a response that is needed for me to know what data to show to the user - success or error message.
<form [action]="'https://test-wallet.example.com/checkout/'" ngNoForm method="POST" target="_blank">
<button type="submit">Pay with card</button>
<input name='param1' value='param1'>
<input name='param2' value='param2'>
<input name='param3' value='param3'>
<input name='param4' value='param4'>
<input name='param5' value='param5'>
<input name='param6' value='param6'>
<input name='param7' value='param7'>
<input name='param8' value='param8'>
<input name='param9' value='param9'>
</form>
Option 2 - HttpClient through service
I've also tried making HttpClient POST request inside the Angular app and without NodeJS backend. In that case, I call the Payment Gateway URL directly but with CORS error.
payment.service.ts:
payFunction(parameters: any){
return this._httpClient.post('https://test-wallet.example.com/checkout/'+
'param1='+parameters.param1+
'&param2='+parameters.param2+
'&param3='+parameters.param3+
'&param4='+parameters.param4+
'&param5='+parameters.param5+
'&param6='+parameters.param6+
'&param7='+parameters.param7+
'&param8='+parameters.param8+
'&param9='+parameters.param9
,parameters
,this.httpOptions
)
.catch(err => {
console.log(err);
return Observable.of(err)
})
}
I call the previous service in component:
async test(form){
await this._myPaymentService.payFunction(form.value).subscribe(res => {
console.log(res);
})
In that case I received only CORS error.
Option 3 - jQuery AJAX
I'm calling this inside my Angular component with cross-domain contentType.
But I also received only CORS error as in the case above. I know that using jQuery in the Angular app is not by the book but I had to try.
$.ajax({
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
},
url : 'https://test-wallet.example.com/checkout/',
type: "POST",
beforeSend: function(xhrObj){
xhrObj.setRequestHeader('Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8');
},
dataType : "json",
async:true,
crossDomain:true,
data: corvusDataObject,
error: function () {
alert('Ajax Error');
},
onFailure: function () {
alert('Ajax Failure');
},
statusCode: {
404: function() {
alert("Ajax 404");
}
},
success : function (response) {
alert("Success: " + JSON.stringify(response));
}
})
.done(function( data ) {
alert("Done: " + JSON.stringify(response));
});
Option 4 - NodeJS/ExpressJS backend
If I use this approach then I received a redirect in the same way as in the first case. But my backend doesn't receive any response from the payment gateway provider.
In Angular app I'm calling my API:
<form [action]="'http://localhost:8080/myPaymentAPI/'" ngNoForm method="POST" target="_blank">
<button type="submit">Pay with card</button>
<input name='param1' value='param1'>
<input name='param2' value='param2'>
<input name='param3' value='param3'>
<input name='param4' value='param4'>
<input name='param5' value='param5'>
<input name='param6' value='param6'>
<input name='param7' value='param7'>
<input name='param8' value='param8'>
<input name='param9' value='param9'>
</form>
In NodeJS/ExpressJS I've made myPaymentAPI API with 307 redirects (from this SO answer).
var express = require('express');
var app = express();
var cors = require('cors') // CORS
var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cors());
var port = process.env.PORT || 8080;
var apiRoutes = express.Router();
apiRoutes.get('/', function(req, res) {
res.json({ message: 'API works!' });
});
app.use('/api', apiRoutes);
app.post('/myPaymentAPI', function(req, res, next) {
let param1 = req.body.param1;
let param2 = req.body.param2;
let param3 = req.body.param3;
let param4 = req.body.param4;
let param5 = req.body.param5;
let param6 = req.body.param6;
let param7 = req.body.param7;
let param8 = req.body.param8;
let param9 = req.body.param9;
res.status(200).redirect(307, 'https://test-wallet.example.com/checkout/?param1='+param1 +'&param2='+param2+...)
//res.end();
});
Above redirection transfers the user to URL (see the first image): https://test-wallet.example.com/#/checkout/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx and user on that URL make a payment but I, once again, don't receive any response.
Option 4.1
fetch returns HTML page but with blank <body>
app.post('/myPaymentAPI', function(req, res, next) {
const url = 'https://test-wallet.example.com/checkout/?param1='+param1+'&param2='+param2+'&param3='+param3+'&param4='+param4+'&param5='+param5+'&param6='+param6+'&param7='+param7+'&param8='+param8+'&param9='+param9;
fetch(url, {
method : "POST",
body: res.body
}).then(
response => response.text()
).then(
html => res.send(html)
).catch((err) => {
reject(err);
});
});
Option 4.2
In this approach, I successfully get a short version of the URL (see the first image) and then I redirect the user to that URL.
app.post('/myPaymentAPI', function(req, res, next) {
let param1 = req.body.param1;
let param2 = req.body.param2;
let param3 = req.body.param3;
...
try{
var body = JSON.stringify(req.body);
const url = 'https://test-wallet.example.com/checkout/?param1='+param1+'&param2='+param2+...;
var newData = await fetch(url, {method: "POST", body: body})
console.log(newData.url)
res.redirect(307, newData.url);
}catch(error){
console.log(error)
}
});
This page is opened after 307 redirects. The message says "Your request cannot be processed. We are sorry, an error occurred."
Do I need in this step once again append FormData before making that redirect?
Option 4.3
In this approach, I'm making a call to my API and create an object inside res.send which then I send to my frontend.
try{
var body = JSON.stringify(req.body);
const url = 'https://test-wallet.example.com/checkout/?param1='+param1+'&param2='+param2+'&param3='+param3+...;
await fetch(url, {method: "POST", body: body}).then((response) => {
const data = response;
res.send({
success: true,
redirectURL: data.url,
body: req.body
})
})
.catch((error) => {
console.error(error);
})
}catch(error){
console.log(error)
}
On frontend I successfully receive redirectURL and body data and try to make a redirect.
this._myPaymentService.payFunction(form.value).subscribe(res => {
console.log(res);
console.log(res.redirectURL);
window.location.replace(res.redirectURL);
})
The web browser then goes to the following page with blank content.
Because request has become GET. I know that it's not possible to send a POST request this way and I'm looking for way to do it.
Wow, sounds like you are very eager to write code but are really lacking some fundamentals. Do you want to have an SPA or have an old school form POST? Of course you get an CORS error when you try to send an direct API request.
I am quite worried about the outcome of this since you are actually dealing with payments and dont seem to know much about architecture - maybe i'm wrong. Did you hear about OWASP or CSRF? Did you think about storing transactions just in case something bad happens? Did you protect against users sending bad requests with i.e. negative numbers? What about
Give yourself and the pockets of your users some comfort and read up first before writing code, go through at least some examples, i.e. Angular Tour of heroes.
Here is the basic flow of how it should look like.
The backend is the translator here. It provides an API, transforms data that the user sent (after validation) into a request that the payment provider needs. After getting the result it will transform the answer into a defined response to the Angular app - something which will be a success or error message. Then the Angular app can decide what to do: Show a ok or error message to the user.
And! You always get a message from the payment provider, if really not then you should implement a timeout and react with an error message to the user.
Good luck, i really pray that you learn about and implement some security measures.
These 2 approach are seems correct:
Option 1
Option 4 (with nodejs server - before 4.1 where payment is successful)
However, there is a flow which seems missing. After the payment is made, the Payment API server does a post request to http://example.com/success or http://example.com/cancel and in the body you find the parameters. So, you can't directly use the url to show user the information on the screen (client side browser).
What you need to do is:
Have the node server (or your backend API server will also work), and use app.post handle the url at the server - the way you are doing for app.post('/myPaymentAPI',).
Update your database or get the relevant payment details or id from req.body etc.
Make a new url like https://yourwebsite.com/payment?status=SUCCESS&other-info or https://yourwebsite.com/payment/id
Redirect user to particular url on browser
That particular url will have the details or id. You can show the relevant details or get the id and make the API call as needed
app.post("http://example.com/success", function(req, res){
//get the req.body/params here which Payment Server will post to success url
//update your backend etc about payment status etc
//redirect to your custom page from here https://yourwebsite.com/payment?status=success&id=id or similar
})
app.post("http://example.com/cancel", function(req, res){
//get the req.body/params here which Payment Server will post to cancel url
//update your backend etc about payment status etc
//redirect to your custom page from here https://yourwebsite.com/payment?status=failure&id=id
})
Hope it helps. Revert for any doubts/clarifications

Node.js API / server post request

I started Node.js and general web few times ago and now I have a project where I have to develop a web server with an API in nodejs running on Express and I want to do it properly but I'm not sure if there's a the good way to do it.
It concerns about every post requests I'm sending to my API, here's an example :
Sign up :
I have a route app.get('/signup') and I'm sending a post request to my API to create new user. So with my guess I send a form with action = /signup method = "post" which means that on my server I've app.post('/signup'). In this post, I create a post request with all the information in my form and I send it to my API which creates the user and send back to the app.post('/signup') the answer. So basically, here the schema:
Get('/signup') -> Post('/signup') -> API('createUser') -> Post('signup')
Then I would like to go back to my app.get('/signup'), because I don't want to send html form the post one, and say "Hey it's ok" instead of showing the form so i'm doing a redirect but how could I know what to display in Get('/signup') ? Can I send a variable to know what to show to the user ? And btw, is it the proper way to do it ?
Thanks !
PS : it doesn't look very clear so here the code :
app.get('/signup', function(req, res) {
res.setHeader('Content-Type', 'text/html');
if (req.session.userId) { res.end("Connected as " + req.session.name); }
else {
res.end('<html><p>As a guest, you\'re signing up : </p><form action="/signup" method="post">' +
'First name: <input type="text" name="firstname"><br>' +
'Last name: <input type="text" name="lastname"><br>' +
'email: <input type="text" name="email"><br>' +
'password: <input type="text" name="password"><br>' +
'role: <input type="text" name="role"><br>' +
'<input type="submit" value="Submit">' +
'</form></html>');
}
});
app.post('/signup', function(req, res) {
request.post({
url: 'http://localhost:4242/signup',
form : {
firstname : req.body.firstname,
lastname : req.body.lastname,
email : req.body.email,
role : req.body.role,
password : req.body.password
}},
function (error, response, body) {
res.redirect('/signup');
});
});
Is it the good way to do it ?
Is there another way to send variable from post to get except the session variable ? (with the redirect) ?
I'm not sure I fully understand the question, but this is how I handle form submits with node.js.
Submit the form using jQuery/Ajax, so we can then get a response from the server after the POST.
$('#your-form').on('submit', function(e) {
e.preventDefault();
var formData = $(this).serializeArray();
$.ajax({
type: 'POST',
url: '/signup',
data: formData
}).success(function(response) {
if (response.success == '1') {
// Do something
} else {
// Do something - e.g. display an error message
}
});
});
Then on your Node.js server, you can handle the app.post('/signup') request, and once you're finished with your API calls, return a status message:
res.json({message: 'User created successfully.', success: '1'});
You can then use this JSON response to take the appropriate action.

Resources