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.
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.
1import { NextRequest, NextResponse } from 'next/server';2 3export async function POST(request: NextRequest) {4 // This is a secure server-side environment5 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 here11 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.
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: text23 })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.
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.
1'use server';2 3import { v4 as uuidv4 } from 'uuid'; // npm install uuid4 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': idempotencyKey20 },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}