Zero
Zero
Back

Send Emails from Next.js using SendGrid

Keep your users engaged with transactional email, powered by one of the most popular email services.

Sam Magura

Sam Magura

A mailbox

Virtually all production web applications need to send transactional emails, e.g. for password reset, notifications, and order confirmations. SendGrid is one of the most popular services for sending transactional email, and it comes with an easy-to-use API. In this post, I'll walk you through building a Next.js application that sends email via SendGrid. We'll utilize the Zero secrets manager to securely retrieve the SendGrid API key at runtime.

In a previous post , I covered sending transactional email with Mailchimp ― that's another option you can consider for your transaction email needs. Though, in my experience, there is not much of a difference between the two services. An email is an email, after all.

🔗 The full code for this example is available in the zerosecrets/examples  GitHub repository.

Secure your secrets conveniently

Zero is a modern secrets manager built with usability at its core. Reliable and secure, it saves time and effort.

Zero dashboard

Signing Up for SendGrid

You can sign up for SendGrid by following this link . Before sending an email, we'll need to configure a verified sender (an email address that we've proved ownership of). To do this, log in to the SendGrid console and select Settings > Sender Authentication in the left-hand side menu. Click "Verify a single sender", enter your personal email address, and complete the verification.

Now, we can create an API key by selecting Email API > Integration Guide from main menu. For the integration method, select Web API, and then Node.js. This will open a step-by-step integration guide, with a form for creating your API key. Create an API key and copy it to a temporary text file on your PC.

Next, create a Zero project to house the SendGrid API key. You should copy the project's token to your temporary text file, as we'll need to provide it to the Next.js app as an environment variable. Back in the Zero console, add a new secret and paste in the SendGrid API key:

Creating a SendGrid secret in Zero
Creating a SendGrid secret in Zero

Creating the Next.js App

To bootstrap a new Next.js application, run

Terminal
npx create-next-app@latest nextjs-sendgrid

At the prompts, select TypeScript and the new App Router.

The example use case I'll be implementing is a purchase confirmation email, like you would find in an ecommerce site. So, let's create a simple HTML form that requests the user's email address, where they would like to receive the confirmation email.

Here's my barebones form:

app/page.tsx
export default function Home() { const [email, setEmail] = useState('') const [isSuccess, setIsSuccess] = useState(false) async function submit() { // TODO } return ( <main className={styles.main}> <h4 className={styles.heading}>Enter your email address to confirm your purchase:</h4> <form onSubmit={(e) => { e.preventDefault() submit() }} noValidate className={styles.form} > <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} className={styles.input} /> <button type="submit">Submit</button> </form> {isSuccess && <p>Success!</p>} </main> ) }

With a few CSS tweaks, this is what it looks like:

The purchase confirmation form
The purchase confirmation form

In the next section, we'll create an API method at /api/purchase, so we can fill in the submit function to call that API method.

app/page.tsx
async function submit() { const response = await fetch('/api/purchase', { method: 'POST', body: JSON.stringify({email}), headers: {'Content-Type': 'application/json'}, }) setIsSuccess(response.ok) }

Creating a Next.js Route Handler

With the old Next.js Pages Router, API routes  allowed you to expose a public API from within a Next.js web application. With the new App Router, API routes have been replaced by Route Handlers . That said, if you're already familiar with API routes, you'll find that the developer experience with Route Handlers is almost the same.

Next.js uses filesystem routing, so to create the /api/purchase API method, you'll create the directories app/api and app/api/purchase. Within the purchase directory, create a route.ts — this is where the Route Hander code will go. To start, let's just log the email address:

app/api/purchase/route.ts
export async function POST(request: Request) { const {email} = await request.json() console.log(`Sending email to: ${email}`) return Response.json(null) }

Authorizing the SendGrid API Client

The easiest way to send an email via the SendGrid web API is to use the @sendgrid/mail  npm package. We'll need to pass the SendGrid API key to @sendgrid/mail, so that's where Zero comes in. Retrieving the API key from Zero is very straightforward, thanks to the Zero TypeScript SDK .

To install both packages, run

Terminal
npm install @sendgrid/mail @zerosecrets/zero

Now, create a new file in the app/api/purchase directory which pulls the secret down from Zero and passes it to the SendGrid API client:

app/api/purchase/initializeSendGrid.ts
import sgMail from '@sendgrid/mail' import {zero} from '@zerosecrets/zero' let initialized = false export async function initializeSendGrid() { if (initialized) { return true } if (!process.env.ZERO_TOKEN) { throw new Error('Did you forget to set the ZERO_TOKEN environment variable?') } const secrets = await zero({ token: process.env.ZERO_TOKEN, pick: ['sendgrid'], }).fetch() if (!secrets.sendgrid) { throw new Error('Did not receive an API key for SendGrid.') } sgMail.setApiKey(secrets.sendgrid.api_key) initialized = true }

This code follows our standard pattern for initializing API clients with Zero.

Completing the Route Handler

Now, all that's left to do is update the API Route Handler to call the function we defined in the previous section, and send the email. The completed Route Handler looks like this:

app/api/purchase/route.ts
export async function POST(request: Request) { const {email} = await request.json() await initializeSendGrid() console.log(`Sending email to: ${email}`) await sgMail.send({ to: email, from: 'YOUR_VERIFIED_SENDER@example.com', subject: 'Purchase confirmation', text: 'This is a sample email.', }) return Response.json(null) }

Make sure to replace YOUR_VERIFIED_SENDER@example.com with the email address you verified when configuring the SendGrid verified sender.

Running the application works like normal, except you need to remember to pass the Zero token in as an environment variable:

Terminal
ZERO_TOKEN='YOUR_ZERO_TOKEN' npm run dev

Now, if you enter your email address in the HTML form and click submit, you should receive an email!

Wrapping Up

Integrating with third party APIs can be a lot of work, but that's not the case with SendGrid. This post shows how you can get up and running with their API in just a few lines of code. Zero also comes in handy here by allowing you to store the SendGrid API key in a single secure location, which could also be accessed by other components of your application, e.g. background jobs that send notification emails.


Other articles

Old-school mailboxes at a post office

Using Notion as a Human-Readable Database

Capture form submissions from your web app and store them where your team works.

A door handle

Use Pusher to Implement Real-Time Notifications

Pusher makes it easy to add pub-sub functionality to your web apps, allowing you to implement chat, notifications, and more.

Secure your secrets

Zero is a modern secrets manager built with usability at its core. Reliable and secure, it saves time and effort.