Home > database >  Sendgrid emails won't send when deployed to Vercel
Sendgrid emails won't send when deployed to Vercel

Time:09-21

Recently, I decided to try out vercel for a small project and ran into issues using sendgrid with vercel. I've implemented sendgrid into nodejs/express projects before on heroku with no issues, but for some reason Vercel just doesn't work.

Even worse, sendgrid gives no errors whatsoever so its been difficult to troubleshoot. The promise just hangs forever. Everything works fine locally when testing and I've confirmed that env variables are correct and loading.

Here's my code:

const sgMail = require('@sendgrid/mail');
const { magicLinkTemplate } = require('./templates');

const sendEmail = async (msg) => {
    sgMail.setApiKey(process.env.SENDGRID_API_KEY);

    try {
        await sgMail.send(msg)
    } catch (error) {
        console.error(error);

        if (error.response) {
        console.error(error.response.body)
        }
    }
}

I've tried a number of iterations, and failed every time. Has anyone else run into issues using vercel with sendgrid?

CodePudding user response:

Alright, so I believe I solved my own problem.

The issue lies in how vercel handles serverless functions:

  • If you're on free tier, each request must resolve in 10 seconds (for pro its 60)
  • Vercel seems to instantly terminate a serverless function once a response is returned, aka side effects that are not finished will cease to run.

Previously, I was running the email sends as a side effect and the api response would return without waiting for sendgrid to fullfil its promise. Below is a watered down version of my original code.


router.post('/', checkUser)

async function checkUser(req, res, next) {
  const { email } = req.body;

  const magicLink = generateMagicLink()

  sendMagicLink(email, magicLink)
  res.status(200).json({})

}

The sendMagicLink function is async and would continue to run locally, but in vercel it would cease once a status was returned from the api.

New code looks like this...


router.post('/', checkUser)

async function checkUser(req, res, next) {
  const { email } = req.body;

  const magicLink = generateMagicLink()

  await sendMagicLink(email, magicLink)
  res.status(200).json({})

}

Now the serverless function stays alive because it waits for the async process to finish.

This whole thing tripped me up because if you have a persistent server, there would be no issues with the original version. Hope this helps someone!

  • Related