I'm building a simple contact form using Next.js, nodemailer and fetch. While the form works and the target email account is receiving the email from the front end form, the contact submission is hanging in the network with a pending
status. About a minute later, I get a Next.js error reading Unhandled Runtime Error TypeError: Failed to fetch
. I have a feeling it has something to do with how I'm using Fetch. I'm used to Axios, and I took this as an opportunity to get better with Fetch. However I referenced MDN's Fetch page.
Form.js
import { useState } from 'react';
const Form = () => {
const [name, setName] = useState('')
const [email, setEmail] = useState('')
const [message, setMessage] = useState('')
const [submitted, setSubmitted] = useState(false)
const handleSubmit = (e) => {
e.preventDefault();
let data = {
name,
email,
message
}
fetch('/api/contact', {
method: 'POST',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
}).then((response) => {
/// NOT RECEIVING CONSOLE LOG OR RESPONSE STATUS BELOW
console.log('response recieved')
if (response.status === 200) {
console.log('response succeeded')
setSubmitted(true)
setName('')
setEmail('')
setBody('')
}
}).then(data => {
console.log(data)
}).then((error) => {
console.error(error)
})
}
return (
<div >
< form >
< formGroup >
< label htmlFor='name'>Name</label>
< input type='text' onChange={(e)=>{setName(e.target.value)}} name='name' />
</formGroup>
< formGroup >
< label htmlFor='email'>Email</label>
< input type='text' onChange={(e)=>{setEmail(e.target.value)}} name='email' />
</formGroup>
< formGroup >
< label htmlFor='message'>Message</label>
< input type='text' onChange={(e)=>{setMessage(e.target.value)}} name='message' />
</formGroup>
< input type='submit' onClick={(e)=>{handleSubmit(e)}}/>
</form >
</div>
)
}
export default Form;
The fetch above references the contact form below, which is in the api
folder that comes with a standard Next.js project. Refactored this a few times to see if there was any difference, including async/await.
contact.js
export default function (req, res) {
require('dotenv').config();
let nodemailer = require('nodemailer')
const transporter = nodemailer.createTransport({
port: 465,
host: "smtp.gmail.com",
auth: {
user: 'samplenodemailer@email.com',
pass: process.env.password,
},
secure: true,
})
const mailData = {
from: 'samplenodemailer@email.com',
to: 'targetinbox@email.com',
subject: `Test Quote Submission From: ${req.body.name}`,
text: req.body.message + " | Sent from: " + req.body.email,
html: `<div>${req.body.message}</div><p>Sent from:
${req.body.email}</p>`
}
transporter.sendMail(mailData, function (err, info) {
if(err)
{console.log(err)}
else
{console.log(info)}
})
res.status(200)
}
I'm not sure where I'm going wrong here. I have a feeling it has to do with how I'm handling Fetch in Form.js. Any help or advice would be greatly appreciated.
Edit Here are some relevant logs
Terminal: API resolved without sending a response for /api/contact, this may result in stalled requests.
In your API route, res.status(200)
alone won't send a response - you need to call end()
.
export default function (req, res) {
//...
res.status(200).end()
}