Tutorial•4 min read

Send Transactional Email from Next.js with Piisend

Learn how to integrate Piisend's transactional email API into your Next.js application using server-side route handlers or Server Actions. This tutorial covers secure API key handling and sending essential emails like OTPs or welcome messages.

Send Transactional Email from Next.js with Piisend

Section 1

Securely Sending Email from Next.js Applications

When sending transactional email from a Next.js application, it's crucial to protect your API keys. Exposing your Piisend API key directly in client-side code would allow unauthorized access to your email sending capabilities. Instead, you should always send emails from a secure server-side environment, such as a Next.js API Route Handler or a Server Action. This ensures your Authorization: Bearer YOUR_API_KEY header remains private and secure.

typescript
1import { NextRequest, NextResponse } from 'next/server';
2
3export async function POST(request: NextRequest) {
4 // This is a secure server-side environment
5 const apiKey = process.env.PIISEND_API_KEY;
6 if (!apiKey) {
7 return NextResponse.json({ message: 'API key not configured' }, { status: 500 });
8 }
9
10 // ... email sending logic will go here
11
12 return NextResponse.json({ message: 'Email endpoint reached' });
13}

Section 2

Implementing a Piisend Email API Route Handler

The Next.js App Router makes it straightforward to create API Route Handlers that act as secure backend endpoints. You can create a file like app/api/send-email/route.ts to handle POST requests from your frontend. This route will then make a server-to-server call to the Piisend API, ensuring your PIISEND_API_KEY is never exposed to the browser. This pattern is ideal for handling user-initiated actions like OTP verification email requests or order confirmations.

typescript
1import { NextRequest, NextResponse } from 'next/server';
2
3export async function POST(request: NextRequest) {
4 const apiKey = process.env.PIISEND_API_KEY;
5 if (!apiKey) {
6 return NextResponse.json({ message: 'API key not configured' }, { status: 500 });
7 }
8
9 try {
10 const { to, subject, html, text } = await request.json();
11
12 const piisendResponse = await fetch('https://api.piisend.com/api/v1/emails', {
13 method: 'POST',
14 headers: {
15 'Authorization': `Bearer ${apiKey}`,
16 'Content-Type': 'application/json'
17 },
18 body: JSON.stringify({
19 to: [to],
20 subject: subject,
21 html: html,
22 text: text
23 })
24 });
25
26 if (!piisendResponse.ok) {
27 const errorData = await piisendResponse.json();
28 console.error('Piisend API error:', errorData);
29 return NextResponse.json({ message: 'Failed to send email', error: errorData }, { status: piisendResponse.status });
30 }
31
32 const data = await piisendResponse.json();
33 return NextResponse.json({ message: 'Email sent successfully', data }, { status: 200 });
34 } catch (error) {
35 console.error('Error sending email:', error);
36 return NextResponse.json({ message: 'Internal server error' }, { status: 500 });
37 }
38}

Section 3

Sending Emails with Next.js Server Actions

Next.js Server Actions provide an even more integrated way to handle server-side logic directly from your React components, without needing a separate API route. By marking a function with 'use server', you can call it directly from a client component or another server component, and it will execute securely on the server. This simplifies the architecture for sending transactional email, making your code cleaner and more maintainable. Remember to always use environment variables for your PIISEND_API_KEY.

typescript
1'use server';
2
3export async function sendWelcomeEmail(email: string, name: string) {
4 const apiKey = process.env.PIISEND_API_KEY;
5 if (!apiKey) {
6 throw new Error('Piisend API key is not configured.');
7 }
8
9 try {
10 const piisendResponse = await fetch('https://api.piisend.com/api/v1/emails', {
11 method: 'POST',
12 headers: {
13 'Authorization': `Bearer ${apiKey}`,
14 'Content-Type': 'application/json'
15 },
16 body: JSON.stringify({
17 to: [email],
18 subject: `Welcome to Piisend, ${name}!`,
19 html: `<p>Hello ${name}, welcome to Piisend! We're excited to have you.</p>`,
20 text: `Hello ${name}, welcome to Piisend! We're excited to have you.`
21 })
22 });
23
24 if (!piisendResponse.ok) {
25 const errorData = await piisendResponse.json();
26 console.error('Piisend API error:', errorData);
27 return { success: false, error: errorData };
28 }
29
30 const data = await piisendResponse.json();
31 return { success: true, data };
32 } catch (error) {
33 console.error('Error sending welcome email:', error);
34 return { success: false, error: 'Internal server error' };
35 }
36}

Section 4

Leveraging Templates and Idempotency for Robust Emails

For more dynamic and consistent transactional emails, Piisend's email templates are invaluable. Instead of sending raw HTML, you can define templates in your Piisend dashboard and pass template_id and template_vars in your API request. This separates content from code and allows for easy updates. Additionally, for critical emails like order confirmations, use the Idempotency-Key header to prevent duplicate sends if a network error causes a retry. This ensures your users receive exactly one email for each unique event.

typescript
1'use server';
2
3import { v4 as uuidv4 } from 'uuid'; // npm install uuid
4
5export async function sendTemplatedEmail(email: string, orderId: string, customerName: string) {
6 const apiKey = process.env.PIISEND_API_KEY;
7 if (!apiKey) {
8 throw new Error('Piisend API key is not configured.');
9 }
10
11 const idempotencyKey = `order-confirmation-${orderId}-${uuidv4()}`;
12
13 try {
14 const piisendResponse = await fetch('https://api.piisend.com/api/v1/emails', {
15 method: 'POST',
16 headers: {
17 'Authorization': `Bearer ${apiKey}`,
18 'Content-Type': 'application/json',
19 'Idempotency-Key': idempotencyKey
20 },
21 body: JSON.stringify({
22 to: [email],
23 template_id: 'order-confirmation-template-id',
24 template_vars: {
25 customer_name: customerName,
26 order_id: orderId,
27 order_date: new Date().toLocaleDateString()
28 }
29 })
30 });
31
32 if (!piisendResponse.ok) {
33 const errorData = await piisendResponse.json();
34 console.error('Piisend API error:', errorData);
35 return { success: false, error: errorData };
36 }
37
38 const data = await piisendResponse.json();
39 return { success: true, data };
40 } catch (error) {
41 console.error('Error sending templated email:', error);
42 return { success: false, error: 'Internal server error' };
43 }
44}

Start sending

Ship transactional email in minutes

Create an API key, verify a domain, and send your first message with Piisend.