Skip to main content

Overview

The Resend Mail provider extends your application ports with email sending capabilities using Resend. Resend is a modern email API that simplifies sending transactional emails with better deliverability and built-in analytics.

Installation

npm install @contract-kit/provider-mail-resend resend

Configuration

The Resend provider reads configuration from environment variables:
VariableRequiredDescriptionExample
RESEND_API_KEYYesResend API keyre_abc123...
RESEND_FROMYesDefault sender email address (must be from verified domain)"My App <[email protected]>"

Getting an API Key

  1. Sign up at resend.com
  2. Verify your domain
  3. Create an API key at resend.com/api-keys

Example .env

RESEND_API_KEY=re_abc123...
RESEND_FROM="My App <[email protected]>"

Setup

Basic Setup

import { createServer } from "@contract-kit/server";
import { mailResendProvider } from "@contract-kit/provider-mail-resend";

const app = createServer({
  ports: basePorts,
  providers: [mailResendProvider],
  createContext: ({ ports }) => ({
    ports,
    // ... other context
  }),
  routes: [
    // ... your routes
  ],
});

Type Your Ports

To get proper type inference for the mailer port, extend your ports type:
import type { MailerPort } from "@contract-kit/mail";
import type { Resend } from "resend";

// Your base ports
const basePorts = definePorts({
  db: dbAdapter,
});

// After using mailResendProvider, your ports will have this shape:
type AppPorts = typeof basePorts & {
  mailer: MailerPort<Resend>;
};

API Reference

The provider extends your ports with a mailer property:

sendText(to, subject, text)

Send a plain text email using the default sender address.
await ctx.ports.mailer.sendText(
  "[email protected]",
  "Welcome",
  "Thanks for joining!"
);
Parameters:
  • to: Recipient email address
  • subject: Email subject
  • text: Plain text content
Returns: Promise<void>

sendHtml(to, subject, html)

Send an HTML email using the default sender address.
await ctx.ports.mailer.sendHtml(
  "[email protected]",
  "Welcome",
  "<h1>Thanks for joining!</h1>"
);
Parameters:
  • to: Recipient email address
  • subject: Email subject
  • html: HTML content
Returns: Promise<void>

send(options)

Send an email with full control over options, including the ability to override the sender.
await ctx.ports.mailer.send({
  from: "[email protected]", // Optional sender override
  to: "[email protected]",
  subject: "Hello",
  html: "<p>Custom email</p>",
});
Parameters:
  • options.from: Sender email address (optional, defaults to RESEND_FROM)
  • options.to: Recipient email address
  • options.subject: Email subject
  • options.text: Plain text content (if not using html)
  • options.html: HTML content (if not using text)
Returns: Promise<void>

client

Access the underlying Resend client for advanced operations.
// Use Resend features directly
const { data, error } = await ctx.ports.mailer.client.emails.send({
  from: "[email protected]",
  to: ["[email protected]", "[email protected]"],
  subject: "Bulk Email",
  html: "<p>Message to multiple recipients</p>",
  tags: [
    { name: "category", value: "newsletter" },
  ],
  attachments: [
    {
      filename: "document.pdf",
      content: pdfBuffer,
    },
  ],
});
Type: Resend

Usage Examples

Send Welcome Email

async function sendWelcomeEmail(ctx: AppCtx, user: User) {
  await ctx.ports.mailer.sendText(
    user.email,
    "Welcome!",
    "Thank you for signing up!"
  );
}

Send Password Reset Email

async function sendPasswordReset(ctx: AppCtx, user: User, resetLink: string) {
  await ctx.ports.mailer.sendHtml(
    user.email,
    "Password Reset",
    `<h1>Reset Your Password</h1>
     <p>Click <a href="${resetLink}">here</a> to reset your password.</p>`
  );
}

Send Custom Email

async function sendCustomEmail(ctx: AppCtx) {
  await ctx.ports.mailer.send({
    from: "[email protected]", // Override default sender
    to: "[email protected]",
    subject: "Custom Email",
    html: "<h1>Hello!</h1>",
  });
}

Advanced: Bulk Emails with Tags and Attachments

const { data, error } = await ctx.ports.mailer.client.emails.send({
  from: "[email protected]",
  to: ["[email protected]", "[email protected]"],
  subject: "Newsletter",
  html: "<h1>Monthly Update</h1>",
  tags: [
    { name: "category", value: "newsletter" },
    { name: "month", value: "december" },
  ],
  attachments: [
    {
      filename: "report.pdf",
      content: pdfBuffer,
    },
  ],
});

if (error) {
  throw new Error(`Failed to send email: ${error.message}`);
}

Lifecycle

The Resend Mail provider:
  1. During register:
    • Creates Resend client with API key
    • Adds the mailer port
  2. During onAppStop: No cleanup needed (Resend client is stateless)

Error Handling

The provider will throw errors in these cases:
  • Missing required environment variables
  • Invalid API key
  • Invalid email addresses
  • Domain not verified in Resend
Make sure to handle these during application startup.

Comparing with SMTP Provider

Resend offers several advantages over traditional SMTP:
  • Simpler setup: Just an API key, no SMTP server configuration
  • Better deliverability: Resend handles infrastructure and reputation
  • Modern features: Built-in analytics, webhooks, and templates
  • No port/firewall issues: Uses HTTPS instead of SMTP ports
Use the SMTP provider (@contract-kit/provider-mail-smtp) if you need to:
  • Use your existing SMTP server
  • Have strict data residency requirements
  • Integrate with legacy email systems

Best Practices

Make sure your domain is verified in Resend before sending production emails.
Consider using Resend’s template features for consistent email branding.
Always handle email sending errors and have a fallback strategy.
Use Resend’s analytics dashboard to monitor email deliverability and engagement.
Tag your emails with categories or campaigns for better analytics and filtering.

Next Steps