Problems with Post req (NodeJs/Express) - sending an empty object? - node.js

I am having some issues with a Post request. I am trying to post a js object, the http request works but it doesnt seems to get the values form the input fields, it posts instead an empty object! I have tried everything cant seems to find the error!
app.js
function addPoem() {
event.preventDefault();
let title = document.getElementById("titleInput").value;
let author = document.getElementById("authorInput").value;
let text = document.getElementById("textInput").value;
let newPoem = {
id: 8,
title: title,
author: author,
text: text,
};
makeRequest("/poems/", "post", newPoem);
}
async function makeRequest(url, reqMethod, body) {
const response = await fetch(url, {
//headers = { "Content-Type": "application/json" },
method: reqMethod,
body: JSON.stringify(body),
});
console.log(response);
const data = await response.json();
console.log(data);
}
index.html
<form method="post" action="/poems">
<h1>Send us your poem!</h1>
<input type="text" requirede name="title" id="titleInput"> <br>
<input type="text" required name="author" id="authorInput"> <br>
<br>
<input type="text" required name="text" id="textInput" style="width:500px;height:500px">
<br>
<button type="submit" onclick="addPoem()">Send</button>
</form>
server.js
// Post a poem
app.post("/poems", (req, res, next) => {
allPoems.push(req.body);
res.json({ status: "A new poem has been posted!" });
});

I think your method should work, because you post directly from the server.js method
But if you yang to use the function in app.js use this
form method="post" action="/poems"
To
form method="post" onsubmit="addPoem()"

Related

Authorization/CORS issue setting up sendgrid form with Gatsby Functions

I'm trying to set up a form that sends info to my email using sendgrid and Gatsby Functions.
I've tried following the gatsby sendgrid example, and the youtube video "four real world solutions using gatsby functions..." but with both I end up getting a authorization/CORS error:
*console error
*response.body.errors
*verified single sender
I've tried a few things like:
adding cors middleware around my function (although I wasn't 100% I did this right)
setting res.set({"Content-Type": "application/json" Authorization: `Bearer ${process.env.SENDGRID_API_KEY}` });
setting res.setHeader('Access-Control-Allow-Origin', '*')
creating new api keys
testing gatsby develop & build, clearing cache etc
I'm running out of ideas so any advice would be appreciated :)
code for current form and api is below:
src/api/sendgrid.js
const sgMail = require('#sendgrid/mail')
console.log("api key:" + process.env.SENDGRID_API_KEY, process.env.SENDGRID_AUTHORIZED_EMAIL)
sgMail.setApiKey(process.env.SENDGRID_API_KEY)
export default async (req, res) => {
res.set({"Content-Type": "application/json",
Authorization: `Bearer ${process.env.SENDGRID_API_KEY}`
});
const msg = {
to: 'myemail#gmail.com', // Change to your recipient
from: process.env.SENDGRID_AUTHORIZED_EMAIL, // Change to your verified sender
subject: 'Sending with SendGrid is Fun',
text: 'and easy to do anywhere, even with Node.js',
html: '<strong>and easy to do anywhere, even with Node.js</strong>',
}
sgMail
.send(msg)
.then(() => {
console.log('Email sent'); console.log(msg);
})
.catch((error) => {
console.error(error);console.log('there was an error');
return res.status(500).json({
error: error.response,
})
})
return res.status(200)
}
src/pages/components/contact.js
const Contact = () => {
const [serverState, setServerState] = useState({
formSent: false,
});
const {
register,
handleSubmit,
formState: { errors },
} = useForm()
const onSubmit = data => {
fetch(`/api/sendgrid`, {
method: `POST`,
body: JSON.stringify(data),
headers: {
"content-type": `application/json`,
},
})
.then(res => res.json())
.then(body => {
console.log(`response from API:`, body);
})
}
console.log({ errors })
return(
<div style={{ display: "grid", width: "100%", }} id="contactSection" >
<div
style={{
// By using the same grid area for both, they are stacked on top of each other
gridArea: "1/1",
position: "relative",
// This centers the other elements inside the hero component
placeItems: "center",
display: "grid",
width: "100%",
}}
>
<ContactDiv>
{/* class={serverState.formSent ? "sent" : ""} */}
<span
// css={styles(serverState.formSent)}
>
<h1>Message Sent</h1>
<p>I'll be in touch shortly. Regards, Daniel.</p>
</span>
{/* <MobileId id="contactM"/> */}
<h1 id="contactM">Need a Website?</h1>
<ContactInfo>
<p>For a free project consultation call, email or use the form below.</p>
<p>Mobile:<br/> 022 078 0868</p>
<p>Email:<br/> daniel#thoughtfulhq.com</p>
</ContactInfo>
<div>
<form
onSubmit={handleSubmit(onSubmit)}
// action="/api/sendgrid" method="POST"
>
<label htmlFor="name">
<p>Name:</p>
<input
type="text"
name="name"
required
{...register("Name", { required: true, maxLength: 100 })}
/>
</label>
<label htmlFor="email">
<p>Email:</p>
<input
type="email"
name="email"
required
{...register("Email", { required: true, pattern: /^\S+#\S+$/i })}
/>
</label>
<label htmlFor="message">
<p>Project Details:</p>
<textarea
name="message"
id="message"
rows="5"
required
{...register("Message", { required: true, maxLength: 2000 })}
/>
</label>
<button type="submit">Submit</button>
</form>
</div>
</ContactDiv>
</div>
</div>
)
}
export default Contact;
Twilio SendGrid developer evangelist here.
As we worked out in the comments, your SendGrid API key was being stored incorrectly in your .env file. Environment variables should be stored on a new line per variable with no spaces between the variable name and the value, like:
SENDGRID_API_KEY=SG.xxx
SENDGRID_AUTHORIZED_EMAIL=sender#example.com
As an extra note, you should not set an Authorization header on the response to your request to the front-end. This will expose your SendGrid API key to anyone who uses the form if they investigate the response headers. I would recommend removing the line:
res.set({"Content-Type": "application/json",
Authorization: `Bearer ${process.env.SENDGRID_API_KEY}`
});
from src/api/sendgrid.js.

Redirect to an external url and send data as body params using express and react

I have a form in the frontend which post it's data to the API in the backend. Then, I add additional parameters i.e. private keys to it and make a JSON object. After this, I have to redirect to an external URL(Payumoney) while passing the JSON object to make a payment.
I can't add the data as query parameters as it contains sensitive information.
App.js (form)
<form action="/api/payumoney" method="POST">
<label>
FirstName:
<input type="text" name="firstname" onChange={handleChange} value={formValue.firstname} />
</label>
<label>
TxnID:
<input type="text" name="txnid" onChange={handleChange} value={formValue.txnid} />
</label>
<label>
Amount:
<input type="text" name="amount" onChange={handleChange} value={formValue.amount} />
</label>
<label>
ProductInfo:
<input type="text" name="productinfo" onChange={handleChange} value={formValue.productinfo} />
</label>
<label>
Email:
<input type="email" name="email" onChange={handleChange} value={formValue.email} />
</label>
<input type="submit" value="Submit" />
</form>
index.js
app.post("/api/payumoney", urlencodedParser, (req,res) => {
const pd = req.body; //JSON Object that we've to parse to the external URL
const url = "https://sandboxsecure.payu.in/_payment";
//Here goes something that can redirect to the URL and pass the JSON object
}
index.js (using request) -- It does the job of redirection and passing the data to it but the payment feature doesn't work
(image attached)
app.post("/api/payumoney", urlencodedParser, (req,res) => {
const pd = req.body; //JSON Object that we've to parse to the external URL
const url = "https://sandboxsecure.payu.in/_payment";
request.post({
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
uri: 'https://sandboxsecure.payu.in/_payment', //Testing URL
form: pd,
}, function (error, httpRes, body) {
if (error){
console.log("Error",error);
res.status(400).json(
{
status: false,
message: error
}
);
}
if (httpRes.statusCode === 200) {
res.send(body);
} else if (httpRes.statusCode >= 300 && httpRes.statusCode <= 400) {
res.redirect(httpRes.headers.location.toString());
console.log("error 300 and 400");
}
})
}
After using request to parse data and redirect to Payumoney URL :
I'm using proxy to have the same origin for both client and server endpoints.
Thank you!

Express.js not sending html file on post request

I'm just trying to send HTML file upon POST request. I'm 100% sure it was working an hour ago. Since then, I cannot figure out why it's not working all of a sudden!
Server Router:
const express = require('express');
const router = express.Router();
const cors = require('cors');
const path = require('path');
const auth = require('../middleware/auth.js');
// HOME ROUTE
router.options('/', cors());
router.get('/', cors(), (req, res) => {
res.status(201).sendFile(path.resolve(__dirname, '../', '../', 'public', 'index.html'));
});
router.post('/', cors(), (req, res) => {
res.status(201).sendFile(path.resolve(__dirname, '../', '../', 'view', 'manager.html'));
});
There's no error from server.
index.html
<form method="POST" autocomplete="off">
<input id="username" type="text" name="username" placeholder="Username" onchange="updateUsername(event)"><br>
<input id="password" type="password" name="password" placeholder="Password" onchange="updatePassword(event)"><br>
<button onclick="submitFunc(event)">LOGIN</button>
</form>
<script>
let username_value = document.querySelector('#username').value;
let password_value = document.querySelector('#password').value;
function updateUsername(e) {
username_value = e.target.value;
}
function updatePassword(e) {
password_value = e.target.value;
}
async function submitFunc(e) {
e.preventDefault();
let response = await fetch('/', {
headers: { 'Content-Type': 'application/json' },
method: 'POST',
body: JSON.stringify({
username: username_value,
password: password_value
})
});
console.log(response);
}
Please note that the login logic itself is not an issue. I altered my code a lot due to this issue I have.
Upon sending POST request to '/', This is the response that logs in client console:
So the fetching itself seems to work just fine. It's just that new HTML file is not replacing the current HTML file. How would I go about fixing this?
You need to actually read the response. await fetch(...) just gets the headers and leaves a readableStream sitting there with the content waiting for you to read the actual content with response.json() or response.text() depending upon the data type you're expecting.
Change to this:
async function submitFunc(e) {
e.preventDefault();
try {
let response = await fetch('/', {
headers: { 'Content-Type': 'application/json' },
method: 'POST',
body: JSON.stringify({
username: username_value,
password: password_value
})
});
// this assumes the response is text or html,
// use response.json() if the response is json
let data = await response.text()
console.log(data);
} catch(e) {
console.log(e);
// decide what to do here if there was an error with the fetch() call
}
}
You can see the various different methods available for reading the body contents here on MDN.
Also, if you're making a request with fetch(), the response from your server will just come back to your Javascript in your web page. It will NOT automatically display in the browser. If you want it to display in your browser, then either let the form post natively (without Javascript) or you will have to manually code your Javascript to receive the response and then insert it into the page content yourself.

Not able to send contact form email with SendGrid on Nodejs

I trying to implement a contact form on Nodejs and express with SendGrid but it's giving me 403 Forbidden error, but the post request i sent returns 200. i don't know what i'm doing wrong, please i need a help to fix this.
Here is my whole route
const express = require('express')
const router = express.Router()
const ContacForm = require('../models/contact_form')
const fs = require('fs')
const path = require('path')
const sgMail = require('#sendgrid/mail');
const nodemailer = require("nodemailer");
router.get('/new', (req, res) => {
res.render("contact_form/new")
})
router.post('/', (req, res) => {
const output = `
<p>You have a new Request</p>
<h3>Contact Details </h3>
<ul>
<li>Name: ${req.body.name}</li>
<li>Email: ${req.body.email}</li>
/ul>
<h3>Message</h3>
<li>Request: ${req.body.request}</li>
`;
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
const msg = {
to: 'chukwumakingley1#gmail.com',
from: 'chukwumakingley1#gmail.com',
subject: 'Sending with Twilio SendGrid is Fun',
text: 'and easy to do anywhere, even with Node.js',
html: output,
};
sgMail.send(msg, (error, contact)=> {
if(error) {
console.log(error)
res.render("contact_form/new")
}
});
});
and these is the error message response i get on the terminal
ResponseError: Forbidden
at node_modules/#sendgrid/client/src/classes/client.js:105:29
at processTicksAndRejections (internal/process/task_queues.js:97:5) {
code: 403,
message: 'Forbidden',
response: {
headers: {
server: 'nginx',
date: 'Sat, 11 Apr 2020 13:15:25 GMT',
'content-type': 'application/json',
'content-length': '281',
connection: 'close',
'access-control-allow-origin': 'https://sendgrid.api-docs.io',
'access-control-allow-methods': 'POST',
'access-control-allow-headers': 'Authorization, Content-Type, On-behalf-of, x-sg-elas-
acl',
'access-control-max-age': '600',
'x-no-cors-reason': 'https://sendgrid.com/docs/Classroom/Basics/API/cors.html'
},
body: { errors: [Array] }
}
}
and here is my form
<form method="POST" action="/contact_form">
<label>Name</label>
<input type="text" name="name" id="name" placeholder="Enter Your Name">
<label>Email</label>
<input type="text" name="email" id="email" placeholder="Enter Your Email">
<label>Request</label>
<textarea name="request" id="request" placeholder="Enter Your Prayer Request" cols="30"
rows="10"></textarea>
<button type="submit"> Submit </button>
</form>
NOTE: I'm sending my SENDGRID_API_KEY variable and it's coming through
This is the kind of Sendgrid API that i am using
Integrate using our Web API or SMTP Relay
I just figured it out after taking time to research and read through the documentation.
It happened that I needed to do additional authentication called Single Sender Verification etc.
and I changed my code to give me a better understanding of the error
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
const msg = {
to: 'chukwumakingsley1#gmail.com',
from: 'chukwumakingsley1#gmail.com',
subject: 'Hello world',
text: output
};
sgMail
.send(msg)
.then(() => {
//Celebrate
console.log('Email Sent!');
})
.catch(error => {
//Log friendly error
console.error(error.toString());
console.log(output)
//Extract error msg
const {message, code, response} = error;
//Extract response msg
const {headers, body} = response;
});
});
after changing the code, the error message changed to
Forbidden (403)
The from address does not match a verified Sender Identity. Mail cannot be sent until this error is resolved. Visit https://sendgrid.com/docs/for-
developers/sending-email/sender-identity/ to see the Sender Identity
requirements
from this error, I read through the error docs on send-grids.
I think the additional authentication was added last month.
Your post request is returning 200 because you call res.render("contact_form/new") when there IS an error:
if(error) {
console.log(error)
res.render("contact_form/new")
}
Take a look at the body of the response you get from the SendGrid response. It contains an array of errors that may provide more information on why you're receiving a 403.
I had the same problem, the sendgrid from April 6, 2020 changed some criteria for authentication of free tests :(. Now it is necessary to make some configurations.
What solved for me was to follow this tutorial here from them. https://sendgrid.com/docs/ui/sending-email/sender-verification/. Only authorizing a few emails to send requests

How to find payload in hapi

I have the following route in hapijs server. And i am trying to create a new file using ajax.
{
method: 'POST',
path: '/create',
config : {
payload:{
maxBytes: 10*1024*1024,
output:'stream',
parse: true,
allow: 'multipart/form-data'
},
handler: function (request, reply) {
var data = request.payload;
if (data.file) { // undefined always
var name = data.file.hapi.filename;
var path = writePath + '/' + name;
var file = fs.createWriteStream(path);
file.on('error', reply);
data.file.pipe(file);
data.file.on('end', function (err) {
reply({
filename: data.file.hapi.filename,
headers: data.file.hapi.headers
});
});
} else reply(boom.badRequest('No file found. Please try again.'));
}
}
The above code always give data.file as undefined. Anything am i missing?
In hapi documentation http://hapijs.com/api#requests, when output is stream
'stream' - the incoming payload is made available via a
Stream.Readable interface. If the payload is 'multipart/form-data' and
parse is true, fields values are presented as text while files are
provided as streams. File streams from a 'multipart/form-data' upload
will also have a property hapi containing filename and headers
properties.
html code :
<form enctype="multipart/form-data" action="/create" method="post">
<input type="file" id="UniqueFileImporter"/>
<input type="submit"/>
</form>
of course there is no js code, as i simply need to submit the form after selecting a file from system
thanks to Matt Harrison for pointing out mistake, i was missing attribute name in file inputer.
html should be
<form enctype="multipart/form-data" action="/create" method="post">
<input type="file" name="file" id="UniqueFileImporter"/>
<input type="submit"/>
</form>

Resources