Reputation: 387
I'm using Nodemailer to send emails in my serverless Next.js project, deployed in Vercel, which works perfectly in development mode. But I'm having problems in production. No error returned, everything works the same way as is development mode, except I don't receive any email.
I have another project built with React and deployed in Heroku where I send emails the same way and it works fine, development and production, so I understand the problem is with Vercel.
Yes, I enabled "Allow Less Secured Apps" in Google account and yes, I enabled Captcha.
I also read this https://vercel.com/docs/solutions/email but it doesn't really make me understand what I should do in my case. I can see it's a matter of SMTP but I don't know what exactly.
Anybody experienced this kind of problem? How can I fix this?
const transporter = nodemailer.createTransport({
host: "smtp.gmail.com",
port: 465,
auth: {
user: [email protected],
pass: myEmailPass
}
});
const mailOptions = {
from: `${req.body.name} ${req.body.email}`,
to: [email protected],
subject: `${req.body.subject}`,
text: `Text: ${req.body.text}`
}
transporter.sendMail(mailOptions, (err, res) => {
if(err) {
console.log(err);
} else {
console.log("success");
}
});
UPDATE
I changed to SendGrid: made an account, created an API Key, and changed the code like so(instead the one above):
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
const msg = {
to: `[email protected]`,
from: `[email protected]`,
subject: `${req.body.subject}`,
text: `${req.body.text}`
};
sgMail
.send(msg)
.then(() => {
console.log('email sent')
})
.catch((error) => {
console.error("error", error)
});
It logs out "email sent" but I don't receive any email. It's the same problem like with Nodemailer. I'm confused now...
Upvotes: 24
Views: 24229
Reputation: 1
Make sure you visit your deployment page for your project on Vercel and update your environmental variables.
Since you are using nodemailer on the backend, Vercel is not aware of any variables in in the .env file. If you have anu environmental variables in the .env file, you can upload the file it self on vercel and it would generate the configurations for your project:
Upvotes: 0
Reputation: 1
In order to get nodemailer to work in production on Vercel (and other deployment platforms) You must wrap your .sendMail({})
function in async
await
. Like so:
const yourFunctionForSendingMail = async () => {
async function main () {
await transporter.sendMail({
// Rest of your email code here
})
}
await main().catch(console.error)
}
Then make sure you await the imported code called from
await yourFunctionForSendingMail()
Upvotes: 0
Reputation: 11
const transporter = nodemailer.createTransport({
host: smtp.gmail.com,
port: 587,
secure: false,
auth: {
user: '[email protected]',
pass: 'yourpassword'
}
})
export const sendEmail = async (to: string, subject: string, text: string) => {
try {
const info = await transporter.sendMail({
from: 'Minh Pham',
to,
subject,
text
})
console.log('Email sent: ' + info.response)
} catch (error) {
console.error('Error sending email: ', error)
}
}
export const resetPassword = async (req: Request, res: Response, next: NextFunction) => {
await sendEmail('to', 'subject', 'text')
}
=> It works fine on vercel
Upvotes: 1
Reputation: 1
I had the same problem, I solved it only by adding async/await, (make sure to use await transporter) in your case it would be:
const name = async() => { await transporter.sendmail(mailOptions).then(..).catch(..)}
Upvotes: 0
Reputation: 1
i encounter this same problem in nodemailer and nextjs14, and its very frustrating but i think i will not use nodemailer with nextjs14 as it have been on nextjs documentation that they block SMTP connection, here is the link as reference.
https://vercel.com/guides/sending-emails-from-an-application-on-vercel
Upvotes: 0
Reputation: 1536
in case someone bump on this in the future:
for nuxt3 in vercel (is not next but it should apply too)
export default defineEventHandler(async (event) => {
await mailTransport.sendMail(mailOptions);
return {}
}
You need to remove the callback from the nodemailer sendEmail function otherwise it does not return a promise... this cause the script in vercel to be terminated earlier hence not email being sent!
If callback argument is not set then the method returns a Promise object. Nodemailer itself does not use Promises internally but it wraps the return into a Promise for convenience.
also applicable to nodejs
Upvotes: 7
Reputation: 361
I tried all the async/await
responses and didn't work at the beginning. Digging through the real time functions
logs of the app, I noticed that there was an Error: Missing credentials for "PLAIN"
, so all I had to do was add the respective .env
variables to vercel environment variables and it worked. Here's the complete code though:
import type { NextApiRequest, NextApiResponse } from 'next'
type Data = any
const nodemailer = require('nodemailer')
const auth = {
user: process.env.WEB_MAILER,
pass: process.env.WEB_MAILER_PASSWORD,
}
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
const { name, email, subject, message } = req.body
const mailData = {
to: process.env.EMAIL_TO,
from: process.env.WEB_MAILER,
name: name,
subject: subject,
text: `Email: ${email}.\n\nMessage: ${message}`,
html: `<div>Email: ${email}.\n\nMessage: ${message}</div>`,
}
const transporter = nodemailer.createTransport({
host: 'smtp.titan.email',
secure: true,
port: 465,
auth: auth,
})
const server = await new Promise((resolve, reject) => {
// verify connection configuration
transporter.verify(function (error: any, success: any) {
if (success) {
resolve(success)
}
reject(error)
})
})
if (!server) {
res.status(500).json({ error: 'Error failed' })
}
const success = await new Promise((resolve, reject) => {
// send mail
transporter.sendMail(mailData).then((info: any, err: any) => {
if (info.response.includes('250')) {
resolve(true)
}
reject(err)
})
})
if (!success) {
res.status(500).json({ error: 'Error sending email' })
}
res.status(200).json({ success: success })
}
Upvotes: 0
Reputation: 19
In my own case, wrapping my email function with async solved it for me.
eg:
const sendMessage = async(message)=>{
await transporter.sendMail({...options here})
}
Then in my API I called my function using:
await sendMessage('your message')
Upvotes: 1
Reputation: 21
I had a similar issue with Nodemailer but I fixed it by first adding the environment variables in Vercel then commit to the github(It will automatically be uploaded on vercel). So add the variables to vercel first for it to take effect
Upvotes: 2
Reputation: 541
I ran into this issue and managed to fix it and keep using nodemailer by adding in promises with async/await.
const nodemailer = require("nodemailer");
export default async (req, res) => {
const { firstName, lastName, email, message } = JSON.parse(req.body);
const transporter = nodemailer.createTransport({
port: 465,
host: "smtp.gmail.com",
auth: {
user: "[email protected]",
pass: "password",
},
secure: true,
});
await new Promise((resolve, reject) => {
// verify connection configuration
transporter.verify(function (error, success) {
if (error) {
console.log(error);
reject(error);
} else {
console.log("Server is ready to take our messages");
resolve(success);
}
});
});
const mailData = {
from: {
name: `${firstName} ${lastName}`,
address: "[email protected]",
},
replyTo: email,
to: "[email protected]",
subject: `form message`,
text: message,
html: `${message}`,
};
await new Promise((resolve, reject) => {
// send mail
transporter.sendMail(mailData, (err, info) => {
if (err) {
console.error(err);
reject(err);
} else {
console.log(info);
resolve(info);
}
});
});
res.status(200).json({ status: "OK" });
};
Upvotes: 54
Reputation: 81
This problem is really confusing indeed. I've managed to fix this by simply adding async/await. This is because streaming responses (fire-and-forget functions) are not supported by Vercel.
Source: https://vercel.com/docs/platform/limits#streaming-responses
Upvotes: 8
Reputation: 3123
I have already encountered the same problem, nodemailer was not working on vercel but on heroku everything worked perfectly. it is specified in the doc that vercel does not block stmp connections but according to what I have experienced, in practice stmp connections are blocked. what you can do is use an alternative to nodemailer. use sendgrid and it works fine
An article on how integrating Sendgrid with Next.js
Upvotes: 2