DEV Community

Cover image for How to send serverless emails with Next.js and Sengrid.
Kenneth Mark
Kenneth Mark

Posted on

How to send serverless emails with Next.js and Sengrid.

Prerequisites

One of the biggest disappointments I encountered whilst learning web development, is that you can't send emails on the frontend. I later discovered that you needed a server to accomplish this task. Today, luckily enough for us, there are a variety of technologies and libraries that enables us to achieve this without having to set up a server, hosting, ssh just to send an email.

In this tutorial, I will be accepting emails in my inbox sent to me by anyone on my website. There are 3 main parts to this, the frontend, backend and Sendgrid. The frontend is built on the Next.js which includes features such as SSR (server side rendering), ISR(incremental static regeneration) and SSG(static site generation). The main goal of this article is going to be achieve with another of Next.js known as API routes. API routes or serverless functions are functions that are invoked only when called. In Next.js, api routes sits in your application in the pages folder in a folder named api. Each file in that folder exports a default anonymous function and you can make requests to the route by is by making requests to /api/{filename}. The end result should look something like this.

export async = (req, res) => {} 
Enter fullscreen mode Exit fullscreen mode

If you have ever written a server in express before, the code above should look family because it acts exactly as the route handlers for express routes.

Before you proceed ensure you have stored the API keys in the .env file. It should look like this:

EMAIL_API_KEY="your-sendgrid-api-key"
Enter fullscreen mode Exit fullscreen mode

Form

The first step of sending an email, is designing and developing the form as you wish. In my case. I am using ChakraUI with React-hook-form, but you can use any UI component library of your choice. Here's how my form looks like:

<form onSubmit={handleSubmit(sendMail)}>
  <Stack>

    <FormControl >
      <FormLabel htmlFor="subject">Subject</FormLabel>
      <Input id='subject' type="text" name="subject"  {...inputProps} ref={register(setValidation('Subject', false, 2))} />
      <ErrorMessage errors={errors} name='subject' as={<Text color='red.600' />} />
    </FormControl>

    <FormControl>
      <FormLabel htmlFor="name" >Name</FormLabel>
      <Input id='name' type="name" name="name" ref={register(setValidation('Name'))} {...inputProps} />
      <ErrorMessage errors={errors} name='name' as={<Text color='red.600' />} />
    </FormControl>

    <FormControl>
      <FormLabel htmlFor="email" >Email address</FormLabel>
      <Input id='email' type="email" name="email" ref={register({ ...setValidation('Email') })}  {...inputProps} />
      <ErrorMessage errors={errors} name='email' as={<Text color='red.600' />} />
    </FormControl>

    <FormControl>
      <FormLabel htmlFor="message">Message</FormLabel>
      <Textarea id='message' type='textarea' name="message" ref={register(setValidation('Message', true))} {...inputProps} h={300} resize='none' />
      <ErrorMessage errors={errors} name='message' as={<Text color='red.600' />} />
    </FormControl>

    <FormControl>
      <Button type='submit' color='white' bg='gray.900' width='100%' h={55} mt={5}>
        Submit
        </Button>
    </FormControl>
   </Stack>
</form>
Enter fullscreen mode Exit fullscreen mode

Send request to api route

Now send the data from the form to the api route.

const sendMail = async (data) => {

    try {
      await fetch("/api/contact", {
        "method": "POST",
        "headers": { "content-type": "application/json" },
        "body": JSON.stringify(data)
      })

            //if sucess do whatever you like, i.e toast notification
      setTimeout(() => reset(), 2000);
    } catch (error) {
        // toast error message. whatever you wish 
    }

  }
Enter fullscreen mode Exit fullscreen mode

Receiving email and sending it off

Once you have sent the email from your frontend, its time to capture it and send it to yourself. This is achieved by first installing sengrid' via npm install @sendgrid/mail. Once done, ensure you have created a contact.js in folder api folder and paste this in.

import sgMail from '@sendgrid/mail'
import { NextApiRequest, NextApiResponse } from 'next';

sgMail.setApiKey(process.env.EMAIL_API_KEY);

export default async (req: NextApiRequest, res: NextApiResponse) => {
  const { email, subject, message, name } = req.body
  const msg = {
    to: '<your-email@example.com',
    from: email,
    subject,
    name,
    text: message,
  };

  try {
    await sgMail.send(msg);
    res.json({ message: `Email has been sent` })
  } catch (error) {
    res.status(500).json({ error: 'Error sending email' })
  }
}
Enter fullscreen mode Exit fullscreen mode

Thats it, the code for sending the email via Sendgrid is quite simple and self explanatory. We first set the api key for Sengrid and after, we create our handler for the route and extract out the email, subject, message and name and then wrap our sgMail.send in a try/catch block.

Deployment

Before deployment, ensure you code is up on Github or Gitlab, also test it and make sure it runs well locally .After these steps, log into your vercel account and start a new project with that Github repo. And thats it, Vercel will build your app and deploy it statically and give you a url to view the project.

Conclusion

You can repurpose the knowledge and send any type of email you want. Sendgrid allows you to create custom and great looking emails using templates. You can also swap Sengrid for your favourite transactional email sending tools such Mailgun, Postmark, Amazon ses or even use gmail via nodemailers SMTP support

If you like this post, please sign up for my newsletter at https://buttondown.email/kennymark or visit my website for more info at https://kennymark.com

Top comments (7)

Collapse
 
banf profile image
baran

Thanks for this Kenneth, it helped a lot! One question though, when I deploy my website to a domain on Vercel, I get a 500 error. I've setup the environment variable on Vercel (I think I've done it correctly?) but I still don't receive emails 🤔

Any ideas?

Collapse
 
iamabdulazeez profile image
I-am-abdulazeez

Thank you so much sir... i find it hard to implement a loading spinner on ChakraUI Button using isSubmitting from react-hook-form.

It didn't work at all

Example:

Collapse
 
steve-lebleu profile image
Steve Lebleu

Thanks for sharing ! Here a package which can help with email sending in node.js environment. One library for many providers.

GitHub logo konfer-be / cliam

Agnostic transactional email sending in Node.js environment

Cliam

Build Status Coverage Status CodeFactor Grade Requires.io (branch) GPL Licence

Transactional emails with a kick

Agnostic transactional email sending in Node.js environment 💥 💪 💊

> Why ?

To improve and facilitate the implementation, flexibility and maintenance of transactional emailing tasks.

> Features

  • Agnostic transactional email sending using web API or SMTP server. One input, one output.
  • Configuration based, not implementation based : easy switch between different modes.
  • Normalized transactions events.
  • Securized payloads.
  • Customisable default templates.

> Table of contents

> Requirements

  • Node.js >= 14.16.0
  • NPM >= 6.14.11

> Getting started

Install

> npm i cliam --save
Enter fullscreen mode Exit fullscreen mode

Configure

Create a .cliamrc.json file on the root of your project.

> touch .cliamrc.json
Enter fullscreen mode Exit fullscreen mode

Define a minimalist configuration in .cliamrc.json newly created:

{
  "consumer": {
    "domain": "https://www.john-doe.com"
  }
  "mode": {
    "api": {
      "name"
Enter fullscreen mode Exit fullscreen mode
Collapse
 
iamabdulazeez profile image
I-am-abdulazeez • Edited

Thank you so much sir... But i tried to add isLoading to ChakraUI button using React-Hook-Form isSubmitting, it does not work at all

Collapse
 
jrock2004 profile image
John Costanzo

Would this not be insecure since there is no back end to hide your token for Sendgrid?

Collapse
 
j_potter_3434 profile image
John Potter

The title and article mentions "Sendgrid". By any chance, did you mean Sendgrid?

Collapse
 
fescobar profile image
fescobar

If you /api/contact endpoint is public then everybody can request that endpoint and use your smtp server for free. That solution is risky.