How to send email through NEXT.JS

Using cloud based provider Resend

LAI TOCA
3 min readDec 27, 2023
Created by @Bing

Sending an emails to notify some person for somethings were happening or informing some actions need to be taken that became a crucial feature in every system nowadays. The story introduced how to sending a user registration verification email as example for the show case of using cloud base email service provider (Resend) that under NEXT.JS.

Using the cloud based email service could be totally cost nothing (under free plan) but also with some limitation (usage threshold / daily limit…). We could take advantage of just invoke their APIs services and saving our time for consideration of setup email server(s), networking or even infra (security / maintenance).

The step-0.1 would be installed the necessary packages for communication with your cloud based services. Also you could download somethings email html CSS template if it’s necessary.

The step-0.2, you need to setup your base account and settings (API keys management / domains / logs / dashboard…) over your cloud based platform. We won’t be to talk more details for this part as it was various among different service providers (you could refer to their online documents).

The step-1, create template for user registration verification as email’s content (The simply case for demonstration that content request user to verify their email address).

// ../app/components/form/mail/MailValidationTemplate.tsx
import React from 'react'
import {
Body,
Button,
Container,
Head,
Hr,
Html,
Preview,
Section,
Text,
} from '@react-email/components';
import { Tailwind } from '@react-email/tailwind';

interface MailTemplateProps {
id: string
username: string
token: string
}

const MailValidationTemplate = ({
id,
username,
token
}: MailTemplateProps) => {
const verifyUri = `${process.env.NEXTAUTH_URL}/verify-mail?id=${id}&token=${token}`
return (
<Html>
<Head />
<Preview>Hello, {username}, Please verfiy your email.</Preview>
<Tailwind>
<Body>
<Container>
<Section>
<Hr />
<Text>
Welcome to onboard. Click below button to verfiy and active your account.
</Text>
<Button className='bg-slate-300 text-gray-700'
href={verifyUri}
>
Verify Your Email
</Button>
<Hr />
<Text>
</Text>
</Section>
</Container>
</Body>
</Tailwind>
</Html>
)
}

export default MailValidationTemplate

The step-2, create API for sending the mail within the content of validation:

// ../api/mail/send/route.tsx
import MailValidationTemplate from '@/app/components/mail/MailValidationTemplate';
import { NextRequest, NextResponse } from 'next/server';
import { Resend } from 'resend';
import * as bcrypt from 'bcrypt'
import { db } from "@/lib/db"
import ResetPasswordTemplate from '@/app/components/mail/ResetPasswordTemplate';
import { isNullOrUndefined } from '@/lib/utils';

import { EmailType } from '@/types/enums';

// API key that generated from your service provider
const resend = new Resend(process.env.RESEND_API_KEY);

export async function POST(req: NextRequest) {
try {
const body = await req.json()
const { userId, type } = body
const token = await bcrypt.hash(userId, 10)
const expired = Number(process.env.TOKEN_EXPIRE_LIFE_TIME)

const user = await db.user.findUnique({
where: { id: userId }
})

if (!user) return NextResponse.json({ message: "User not found." }, { status: 500 })

const username = user.username
const sendTo = user.email
// store token to db
const newVerificationToken = await db.verificationToken.create({
data:
{
identifier: userId,
token: token,
expires: new Date(Date.now() + expired) // expired one hour
}
})

if (!newVerificationToken) return NextResponse.json({ message: "VerificationToken create failed." }, { status: 500 })

const getMailTemple = (type: EmailType): React.JSX.Element => {
switch (type) {
case EmailType.EmailValidation:
return (MailValidationTemplate({ id: userId, username: username, token: token }))
case EmailType.ResetPassword:
// ....
// return reset mail template...
default:
throw new Error('Mail template not found.')
}
}

const getSubject = (type: EmailType): string => {
switch (type) {
case EmailType.EmailValidation:
return 'Verify Your Mail'
case EmailType.ResetPassword:
return 'Reset Your Password'
default:
throw new Error('Mail template not found.')
}
}

const result = await resend.emails.send({
from: 'No-relpy <onboarding@ducky-studio.xyz>',
to: [`${sendTo}`],
subject: getSubject(type),
react: getMailTemple(type)
});


if (!isNullOrUndefined(result.data)) return NextResponse.json({ result: result, message: 'Mail has been submitted.' });

return NextResponse.json({ result: result, message: "Send email occurred error." }, { status: 500 })

} catch (error: any) {
console.log(error)
return NextResponse.json({ message: "Something went wrong. " }, { status: 500 })
}
}

That’s all. Yet one more thing need to mention, if you don’t have your own domain, you will have restriction to submit (or even permission abandon) your email across default domain (might for the spam email policy, I guessed).

Reference

--

--

LAI TOCA

Coding for fun. (Either you are running for food or running for being food.)