EasyStarter logoEasyStarter

Email Service

Configure Resend to enable sign-up verification and password reset emails

Email Service

EasyStarter uses Resend as its email delivery provider. Two system emails are built in out of the box:

Email typeWhen it is sent
Sign-up verificationAfter registration — the user must click the link to activate their account
Forgot passwordWhen the user requests a password reset — sends a reset link

Sign up for Resend and get an API Key

Official docs: Resend API Keys

  1. Go to resend.com and create an account
  2. After logging in, open the API Keys page
  3. Click Create API Key
  4. Give the key a name, e.g. easystarter-production
  5. Set permission to Sending access (sending permission is all that is needed)
  6. Click Add to finish
  7. Copy the generated API Key — it is only shown once, save it immediately

The copied value is your RESEND_API_KEY. It starts with re_, for example:

re_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Verify your sender domain

Resend requires a verified domain before you can send emails in production.

Official docs: Resend Domains

  1. Log in to Resend and go to the Domains page
  2. Click Add Domain
  3. Enter your domain, e.g. yourdomain.com
  4. Resend will provide a set of DNS records (TXT, MX, DMARC, etc.)
  5. Add these records in your DNS provider (e.g. Cloudflare)
  6. Back in Resend, click Verify DNS Records and wait for verification to pass

Once verified, the sender address will take the form noreply@yourdomain.com (the local part is configurable).

Testing: On Resend's free plan you can send emails to your own registered email address without a verified domain — useful for local testing.

Set RESEND_API_KEY

RESEND_API_KEY must be added to two environment variable files:

Local development (apps/server/.dev.vars):

apps/server/.dev.vars
RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Production deployment (apps/server/.env.production):

apps/server/.env.production
RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

.env.production is used to bulk-push secrets to Cloudflare Workers via pnpm run secrets:bulk:production. It does not participate in the build directly.

Configure the sender address

The sender address is defined inside the common block in packages/app-config/src/app-config.ts:

packages/app-config/src/app-config.ts
common: {
  app: {
    name: "your-app-name",   // Display name shown in the From field — change to your product name
    // ...
  },
  email: {
    provider: "resend",
    from: {
      localPart: "noreply",     // local part of the address (before @)
      domain: "yourdomain.com", // domain verified in Resend
    },
  },
  // ...
},

The resulting From field in the sent email is automatically composed from these two fields:

your-app-name <noreply@yourdomain.com>
  • your-app-name → taken from common.app.name — update this to your own product name
  • noreply@yourdomain.com → built by joining email.from.localPart + @ + email.from.domain

Make sure domain matches the domain you have verified in Resend, otherwise sending will fail.

Built-in email templates

EasyStarter uses React Email to render email templates. The template source files are located at:

apps/server/src/emails/templates/
├── sign-up-verify-email.tsx    # Sign-up verification email
└── forgot-password-email.tsx   # Forgot password email

All email content is i18n-aware and will automatically switch language based on the user's request locale.

To customise the email design or copy, simply edit the corresponding .tsx template file.

Extending to other email providers

EasyStarter's email layer is built around the EmailProvider interface. Plugging in any provider (e.g. SendGrid, Postmark, Mailgun) takes four steps.

Step 1: Register the provider key

Suppose using SendGrid as an example. Add the new provider key to SUPPORTED_EMAIL_PROVIDERS in packages/app-config/src/types.ts:

packages/app-config/src/types.ts
export const SUPPORTED_EMAIL_PROVIDERS = ["resend", "sendgrid"] as const;

Step 2: Implement the provider

Create a new file in apps/server/src/emails/providers/ that implements the EmailProvider interface:

apps/server/src/emails/providers/sendgrid.ts
import type { EmailProvider, SendEmailParams } from "../types";

export function createSendGridEmailProvider(): EmailProvider {
  return {
    key: "sendgrid",
    async send({ from, to, subject, html, text }: SendEmailParams) {
      // Call the SendGrid SDK to send the email
    },
  };
}

Step 3: Register it in the email providers map

Add the new provider to the providers map in apps/server/src/emails/index.ts:

apps/server/src/emails/index.ts
import { createSendGridEmailProvider } from "./providers/sendgrid";

const providers: Record<EmailProviderKey, EmailProvider> = {
  resend: createResendEmailProvider({ defaultFrom }),
  sendgrid: createSendGridEmailProvider(),
};

Step 4: Switch the configuration

Update email.provider in packages/app-config/src/app-config.ts to the new provider key:

packages/app-config/src/app-config.ts
email: {
  provider: "sendgrid", // switch to the new provider
  from: {
    localPart: "noreply",
    domain: "yourdomain.com",
  },
},

Once done, all built-in send functions (sendVerificationEmail, sendResetPasswordEmail) will automatically route through the new provider — no changes to business logic required.