Email System

Email functionality using Resend

Email System

ShipKit uses Resend for reliable email delivery, with support for transactional emails, audience management, and delivery tracking.

Overview

The email system provides:

  • Transactional email sending
  • Audience management
  • Email templates
  • Delivery tracking
  • Error handling
  • Type-safe API

Core Setup

Resend Client

// src/lib/resend.ts
import { env } from "@/env";
import { Resend } from "resend";

/**
 * Initialize Resend with API key if available, otherwise return null
 * This allows the application to build even if RESEND_API_KEY is not set
 */
export const resend = env.RESEND_API_KEY ? new Resend(env.RESEND_API_KEY) : null;

Environment Configuration

Required environment variables:

# Email (Resend)
RESEND_API_KEY="re_123456789"      # API key from Resend dashboard
RESEND_AUDIENCE_ID="aud_123456789"  # Optional: Audience ID for mailing lists

Email Services

Feedback Email Service

// src/server/services/resend-service.ts
import { siteConfig } from "@/config/site";
import { resend } from "@/lib/resend";

export const sendFeedbackEmail = async (content: string): Promise<void> => {
  try {
    if (!resend) {
      console.warn("Resend client not initialized - RESEND_API_KEY not set");
      return;
    }

    const result = await resend.emails.send({
      from: `🍱 ${siteConfig.name} <${siteConfig.email.support}>`,
      to: [siteConfig.creator.email],
      subject: "New Feedback Received",
      html: `<p>${content}</p>`,
    });
    console.log("Feedback sent successfully", result);
  } catch (error) {
    console.error("Error sending feedback:", error);
    throw new Error("Failed to send feedback email");
  }
};

Server Actions

Feedback Action

// src/server/actions/resend-actions.ts
export const sendFeedback = async (content: string): Promise<void> => {
  try {
    await sendFeedbackEmail(content);
  } catch (error) {
    console.error("Error in sendFeedback action:", error);
    throw error;
  }
};

Audience Management

// src/server/actions/resend-actions.ts
export const addAudienceUser = async (email: string) => {
  try {
    if (!env.RESEND_AUDIENCE_ID) {
      throw new Error("Missing Resend API key or audience ID");
    }

    const result = await resend.contacts.create({
      email,
      audienceId: env.RESEND_AUDIENCE_ID,
    });
    return { success: true };
  } catch (error: unknown) {
    if (error instanceof Error) {
      return { success: false, error: error.message };
    }
    return { success: false, error: "An unknown error occurred" };
  }
};

Email Templates

Email templates are defined using HTML with dynamic content injection:

const emailTemplate = (data: EmailData) => `
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>${data.subject}</title>
</head>
<body>
  <h1>${data.title}</h1>
  <p>${data.content}</p>
  <!-- Add more template sections -->
</body>
</html>
`;

Usage Examples

Sending Transactional Email

const sendWelcomeEmail = async (user: User) => {
  if (!resend) return;

  await resend.emails.send({
    from: siteConfig.email.support,
    to: user.email,
    subject: "Welcome to ShipKit",
    html: welcomeEmailTemplate({
      name: user.name,
      // Add more template data
    }),
  });
};

Managing Audience

const subscribeToNewsletter = async (email: string) => {
  const result = await addAudienceUser(email);
  if (result.success) {
    // Handle successful subscription
  } else {
    // Handle error
  }
};

Error Handling

The system implements comprehensive error handling:

  1. Client Initialization

    if (!resend) {
      console.warn("Resend client not initialized");
      return;
    }
    
  2. Service Errors

    try {
      // Email sending logic
    } catch (error) {
      console.error("Error sending email:", error);
      throw new Error("Failed to send email");
    }
    
  3. Action Errors

    if (error instanceof Error) {
      return { success: false, error: error.message };
    }
    return { success: false, error: "An unknown error occurred" };
    

Development Workflow

  1. Local Development

    • Use test API key
    • Log emails instead of sending
    • Monitor email queue
  2. Testing

    • Mock Resend client
    • Verify email content
    • Test error scenarios
  3. Production

    • Use production API key
    • Monitor delivery rates
    • Track bounces and complaints

Performance Considerations

  1. Batch Processing

    • Group similar emails
    • Use rate limiting
    • Implement retry logic
  2. Template Optimization

    • Cache templates
    • Minimize dynamic content
    • Optimize images
  3. Error Recovery

    • Implement backoff strategy
    • Queue failed attempts
    • Log delivery issues

Security Implementation

  1. API Key Management

    • Secure key storage
    • Environment-based keys
    • Regular key rotation
  2. Content Security

    • HTML sanitization
    • Link validation
    • Attachment scanning
  3. Access Control

    • Rate limiting
    • IP allowlisting
    • Audit logging

Best Practices

  1. Email Content

    • Use responsive design
    • Test across clients
    • Follow anti-spam guidelines
  2. Delivery Optimization

    • Set proper headers
    • Include unsubscribe links
    • Monitor engagement
  3. Error Management

    • Implement retry logic
    • Log delivery issues
    • Alert on failures

Notes

  • Resend client is optional (null if API key not set)
  • Templates use HTML with dynamic content
  • Error handling is comprehensive
  • Audience management is optional
  • Development mode includes safety checks