Sending Emails Using AWS SNS and Next.js

Sending Emails Using AWS SNS and Next.js

Hello! In this tutorial, I will go over how to setup very simple email sending functionality in a Next.js application, using just AWS SNS!

Now, this method may not be perfect for all use cases. In my scenario, we are creating a very basic contact form, which will send barebones emails.

What is SNS?

If you are already familiar with how AWS's Simple Notification Service works, then you can skip this section! But for those who are new to it, I'll give a basic rundown.

AWS SNS (Amazon Simple Notification Service) is a fully managed messaging service provided by Amazon Web Services (AWS) that enables you to send messages to a large number of recipients.

It works in a "pub/sub" manner, where you publish to the topic, and subscribers (such as lambda functions, email, SQS queues, etc) will receive these messages.

SNS supports multiple protocols (for aforementioned subscribers), such as:

  • Email: You can send notifications via email. (which is what we'll be doing)

  • SMS: Send text messages to mobile devices.

  • HTTP/HTTPS: Post messages to web servers using HTTP/HTTPS.

  • AWS Lambda: Trigger Lambda functions.

  • SQS (Simple Queue Service): Deliver messages to SQS queues.

  • Mobile push notifications: Send messages to mobile devices via push notifications.

Some common use cases for SNS include application alerts, event-driven architectures, mobile notifications, broadcast messaging, and more. Our use case for this tutorial will be very simple.


Setting up SNS

If you don't have one already, set up an AWS account. You can choose to create a new IAM user for this tutorial if you want. You'll just need the access key ID and secret access key for the account (we'll go over where to find them if you don't know).

First, navigate to the SNS Console. Type in the name of the topic you want to create, and press "Next Step"

On the next page, it will show a bunch of configuration options. You don't need to change anything for this tutorial. Just make sure that the Type of the topic is Standard, not FIFO.

Scroll down and click "Create Topic"

Great, you've created an SNS topic!

Now, under Subscriptions, click "Create subscription".

On the next page, choose "Email" for the Protocol, and type in your email for the Endpoint. Then click "Create Subscription".

Note that you will need to confirm that email before sending messages to it! So do that next. Once your email is confirmed, you can see the status of the Subscription changes.

Navigate back to the page of the Topic you created earlier. You can test that it's working by clicking "Publish message" in the top right.

This will take you to a page where you can write a test message to send.

This is what my message looked like:


Publishing to the Topic from Next.js

At a high level, this is how the flow will work:

  • A user hits the submit button for the contact form

  • The frontend makes an API call to backend with the message

  • Backend API will publish to SNS topic

I'll let you handle the creation of the form and all of its logic.

Make sure you run this command to install the dependencies:

npm i @aws-sdk/client-sns

Next, in the app folder, create a folder called api, and within that folder, another folder called sendEmail. Within that folder, create a file called route.ts

Next, put the following code in route.ts:

import { NextRequest, NextResponse } from 'next/server';
import { SNSClient, PublishCommand } from '@aws-sdk/client-sns';

const snsClient = new SNSClient({
  region: 'us-west-2', 
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
  },
});

export async function POST(req: NextRequest) {
  try {
    const { emailBody } = await req.json();

    const params = {
      Message: emailBody,
      TopicArn: process.env.CONTACT_TOPIC_ARN!, 
    };

    const command = new PublishCommand(params);
    await snsClient.send(command);

    return NextResponse.json({ success: true });
  } catch (error) {
    console.error('Error publishing to SNS:', error);
    return NextResponse.json({ success: false, error: 'Failed to send email' }, { status: 500 });
  }
}

export async function GET() {
  return NextResponse.json({ message: "This endpoint only supports POST requests." }, { status: 405 });
}

Make sure you add the environment variables to your .env.local file:

AWS_ACCESS_KEY_ID=<access_key_id>
AWS_SECRET_ACCESS_KEY=<secret_access_key>
CONTACT_TOPIC_ARN=<arn>

If you don't know where to find your access key ID and secret access key, go to the AWS console and click your name in the top right, then click "Security Credentials".

You'll see this section, where you can create access keys.

Next, we'll send the request from the frontend. I'm assuming you already have a form in place, and most of the logic covered. But at a high level, you just need to make a POST request to the endpoint we just created. Here is an example of what this could look like:

const handleSubmit = async (event: any) => {
        event.preventDefault(); 

        const emailBody = `From: ${name} (${email})\n\n${message}`;

        try {
          const response = await fetch('/api/sendEmail', {
              method: 'POST',
              headers: {
                  'Content-Type': 'application/json',
              },
              body: JSON.stringify({ emailBody }),
          });

          if (response.ok) {
              setName("");
              setEmail("");
              setMessage("");
              setStatus("message sent! thanks!");
          } else {
            setStatus("something went wrong! please try again.");
          }
        } catch (error) {
          console.error('Error sending email:', error);
          alert('Failed to send email');
        }
      }

After that, you're all set! Users can send you an email with a click of a button!

One important thing to consider is protecting your API route, but that is out of the scope of this tutorial for now.

Thanks for reading!

Thanks for reading this article, I hope it helped!