mcpField
R
resend

resend

github.com/vercel-labsテスト・品質2.0k228 回インストール1 日前に更新
リポジトリ

resend について

resendは、Resend Email APIをローカルで状態付きエミュレーションするツールです。メールの送信処理をテストする開発者向けで、実際のメールは送らず、送信内容を受信箱UIやREST APIから確認できます。

主な機能

  1. 01送信メールをメモリに保存
  2. 02受信箱UIまたはRESTでメールを確認
  3. 03ドメイン、APIキー、オーディエンス、連絡先をエミュレート
  4. 04冪等な送信リクエストに対応
  5. 05宛先許可リストと送信試行をテスト

活用シーン

  1. 01実メールを送らずにメール配信フローをテスト
  2. 02テストで認証コードやマジックリンクを抽出
  3. 03宛先拒否と再試行の挙動を確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/vercel-labs/emulate --skill resend

インストール後、Claude Code で/resendを入力して読み込まれたか確認します。

詳細

Resend Email API エミュレーター

Resend API を完全にステートフルにエミュレートします。メール、ドメイン、API キー、オーディエンス、連絡先はメモリに保存されます。送信されたメールは保存され、受信トレイ UI または REST API から確認できます。

実際のメールは送信されません。受け付けられた POST /emails の呼び出しはメッセージをローカルに保存するため、プログラムから、またはブラウザーで確認できます。検証済みの送信では、受信者による拒否を含め、送信試行も記録されます。

起動

ローカルリスナーはデフォルトで 127.0.0.1 にバインドされます。コンテナーやほかのマシンからアクセスするには、CLI に --host 0.0.0.0 を渡すか、createEmulator に hostname: '0.0.0.0' を指定します。それらのクライアントからアクセスできる URL を通知するには、--base-url または baseUrl を設定します。

# Resend only
npx emulate --service resend

# Default port (when run alone)
# http://localhost:4000

または、プログラムから実行します。

import { createEmulator } from 'emulate'

const resend = await createEmulator({ service: 'resend', port: 4000 })
// resend.url === 'http://localhost:4000'

認証

トークンは Authorization: Bearer <token> として渡します。re_ で始まるトークンはすべて受け付けられます。

curl http://localhost:4000/emails \
  -H "Authorization: Bearer re_test_key"

トークンが指定されていない場合、リクエストはデフォルトユーザーにフォールバックします。

アプリからエミュレーターを指定する

環境変数(Resend SDK)

公式の Resend Node.js SDK は、モジュールの読み込み時に RESEND_BASE_URL を読み取ります。これをエミュレーターの URL に設定すると、コードを変更せずに SDK を使用できます。

RESEND_BASE_URL=http://localhost:4000
import { Resend } from 'resend'

// No baseUrl argument needed; the SDK reads RESEND_BASE_URL automatically.
const resend = new Resend('re_test_key')

await resend.emails.send({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Hello',
  html: '<p>It works!</p>',
})
Next.js に組み込む(adapter-next)

@emulators/adapter-next を使用すると、エミュレーターは Next.js アプリ内の /emulate/resend で実行されます。next.config.ts で RESEND_BASE_URL を設定します。

// next.config.ts
import { withEmulate } from '@emulators/adapter-next'

export default withEmulate({
  env: {
    RESEND_BASE_URL: `http://localhost:${process.env.PORT ?? '3000'}/emulate/resend`,
  },
})
// app/emulate/[...path]/route.ts
import { createEmulateHandler } from '@emulators/adapter-next'
import * as resend from '@emulators/resend'

export const { GET, POST, PUT, PATCH, DELETE } = createEmulateHandler({
  services: {
    resend: {
      emulator: resend,
      seed: {
        domains: [{ name: 'example.com' }],
      },
    },
  },
})
fetch を直接使用する

SDK や環境変数を使用できない場合は、エミュレーターを直接呼び出します。

await fetch('http://localhost:4000/emails', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer re_test_key',
  },
  body: JSON.stringify({
    from: '[email protected]',
    to: '[email protected]',
    subject: 'Hello',
    html: '<p>It works!</p>',
  }),
})

シード設定

resend:
  domains:
    - name: example.com
      region: us-east-1
  contacts:
    - email: [email protected]
      first_name: Test
      last_name: User
      audience: Default

送信済みメールの取得

エミュレーターの大きな特長は、POST /emails で送信されたすべてのメールが保存され、クエリ可能なことです。

受信トレイ UI

ブラウザーで送信済みメールを確認します。

http://localhost:4000/inbox
REST API
# List all sent emails
curl http://localhost:4000/emails \
  -H "Authorization: Bearer re_test_key"

# Get a single email by ID
curl http://localhost:4000/emails/<id> \
  -H "Authorization: Bearer re_test_key"
メールからのデータ抽出(テスト、エージェント)

マジックリンク、確認コード、パスワードリセットのワークフローをプログラムから完了する場合に便利です。

# Get the latest email ID
EMAIL_ID=$(curl -s http://localhost:4000/emails \
  -H "Authorization: Bearer re_test_key" | jq -r '.data[0].id')

# Extract a 6-digit code from the HTML body
CODE=$(curl -s http://localhost:4000/emails/$EMAIL_ID \
  -H "Authorization: Bearer re_test_key" | jq -r '.html' | grep -oE '[0-9]{6}')

echo "Verification code: $CODE"

API エンドポイント

メール
# Send an email
curl -X POST http://localhost:4000/emails \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"from": "[email protected]", "to": "[email protected]", "subject": "Hello", "html": "<p>Hi</p>"}'

# Send batch (up to 100)
curl -X POST http://localhost:4000/emails/batch \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '[{"from": "[email protected]", "to": "[email protected]", "subject": "One", "html": "<p>1</p>"}]'

# List all emails
curl http://localhost:4000/emails \
  -H "Authorization: Bearer $TOKEN"

# Get email by ID
curl http://localhost:4000/emails/<id> \
  -H "Authorization: Bearer $TOKEN"

# Cancel a scheduled email
curl -X POST http://localhost:4000/emails/<id>/cancel \
  -H "Authorization: Bearer $TOKEN"

サポートされるフィールド:from、to、subject、html、text、cc、bcc、reply_to、headers、tags、scheduled_at。

冪等な送信

POST /emails と POST /emails/batch は、大文字と小文字を区別しない Idempotency-Key ヘッダーを受け付けます。キーは 1~256 文字で、24 時間有効です。同じキーと検証済みのペイロードで再試行すると、メールを作成したり、重複する email.sent および email.delivered ウェブフックを送信したりせずに、元のレスポンス、メール ID、ステータスを返します。異なるペイロードまたはエンドポイントでキーを再利用すると 409 invalid_idempotent_request が返され、キーの長さが無効な場合は 400 invalid_idempotency_key が返されます。ヘッダーを省略した場合は、通常どおり動作します。

curl -X POST http://localhost:4000/emails \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: signup-email-123" \
  -d '{"from": "[email protected]", "to": "[email protected]", "subject": "Hello"}'
宛先の制限と送信試行

シード設定で resend.allowlist を設定して、宛先の拒否をテストします。

resend:
  allowlist:
    - [email protected]
    - [email protected]

allowlist を省略するとすべての宛先が許可され、allowlist: [] にするとすべての宛先が拒否されます。to、cc、bcc の各アドレスがすべて登録されている必要があります。照合時には前後の空白と大文字・小文字が無視されます。登録できるのは個々のアドレスのみで、ワイルドカードやドメインによる照合には対応していません。これは Resend のドメイン認証やアカウント制限とは独立した、ローカルテスト用の制御です。

送信が拒否されると、拒否されたアドレスと設定済みの許可リストを示す 403 validation_error が返されます。メール ID、メールレコード、冪等性レコード、Webhook は作成されません。宛先のいずれかが許可されていない場合、バッチ全体が拒否されます。同じ冪等性キーを使って再試行した場合も、拒否されたリクエストは新たな試行として扱われます。受理された冪等リクエストを再生した場合は、その後に許可リストが変更されていても、元のレスポンスが返されます。

検証済みの送信では、拒否されたバッチ内のメールも含め、メールごとに1件のレコードが resend.send_attempts ストアコレクションに追加されます。各レコードには、from、to、cc、bcc、subject、idempotency_key(または null)、およびストアの id、created_at、updated_at が含まれます。メッセージ本文は試行レコードにコピーされません。不正なリクエスト、無効または競合する冪等性キー、冪等性リクエストの再生では、試行レコードは追加されません。

@emulators/resend の getResendStore(store).sendAttempts.all() で試行を読み取るか、永続化された状態を確認してください。共有アダプターのスナップショットでは、キー resend:resend.send_attempts が使用されます。試行と宛先制限は、拒否された送信も含め、保存済み状態の復元後も維持されます。試行を取得するための Resend API エンドポイントはありません。拒否された送信にはメールを伴わない試行レコードが残ります。送信自体が行われなかった場合は、どちらも残りません。

ドメイン
# Create domain
curl -X POST http://localhost:4000/domains \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "example.com", "region": "us-east-1"}'

# List domains
curl http://localhost:4000/domains \
  -H "Authorization: Bearer $TOKEN"

# Get domain
curl http://localhost:4000/domains/<id> \
  -H "Authorization: Bearer $TOKEN"

# Verify domain (instantly marks all records as verified)
curl -X POST http://localhost:4000/domains/<id>/verify \
  -H "Authorization: Bearer $TOKEN"

# Delete domain
curl -X DELETE http://localhost:4000/domains/<id> \
  -H "Authorization: Bearer $TOKEN"
API キー
# Create API key
curl -X POST http://localhost:4000/api-keys \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Production"}'

# List API keys
curl http://localhost:4000/api-keys \
  -H "Authorization: Bearer $TOKEN"

# Delete API key
curl -X DELETE http://localhost:4000/api-keys/<id> \
  -H "Authorization: Bearer $TOKEN"
オーディエンス
# Create audience
curl -X POST http://localhost:4000/audiences \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Newsletter"}'

# List audiences
curl http://localhost:4000/audiences \
  -H "Authorization: Bearer $TOKEN"

# Delete audience
curl -X DELETE http://localhost:4000/audiences/<id> \
  -H "Authorization: Bearer $TOKEN"
コンタクト
# Create contact in an audience
curl -X POST http://localhost:4000/audiences/<audience_id>/contacts \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"email": "[email protected]", "first_name": "Jane", "last_name": "Doe"}'

# List contacts in an audience
curl http://localhost:4000/audiences/<audience_id>/contacts \
  -H "Authorization: Bearer $TOKEN"

# Delete contact
curl -X DELETE http://localhost:4000/audiences/<audience_id>/contacts/<id> \
  -H "Authorization: Bearer $TOKEN"

Webhook

エミュレーターは、状態が変更されると Webhook イベントを送信します。

  • POST /emails で email.sent と email.delivered
  • ドメイン操作で domain.created と domain.deleted
  • コンタクト操作で contact.created と contact.deleted

よく使われるパターン

マジックリンク/確認コードのフロー
TOKEN="re_test_key"
BASE="http://localhost:4000"

# 1. Send verification email
curl -X POST $BASE/emails \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"from": "[email protected]", "to": "[email protected]", "subject": "Your code", "html": "<p>Code: <strong>482910</strong></p>"}'

# 2. Retrieve the email
EMAIL_ID=$(curl -s $BASE/emails -H "Authorization: Bearer $TOKEN" | jq -r '.data[0].id')

# 3. Read the HTML body
curl -s $BASE/emails/$EMAIL_ID -H "Authorization: Bearer $TOKEN" | jq -r '.html'
テストでの送信と確認
import { createEmulator } from 'emulate'
import { Resend } from 'resend'

const emu = await createEmulator({ service: 'resend', port: 4000 })

process.env.RESEND_BASE_URL = emu.url
const resend = new Resend('re_test_key')

// Send
await resend.emails.send({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Verify',
  html: '<p>Code: <strong>123456</strong></p>',
})

// Retrieve
const res = await fetch(`${emu.url}/emails`, {
  headers: { Authorization: 'Bearer re_test_key' },
})
const { data: emails } = await res.json()
console.log(emails[0].html) // contains "123456"

組み込みエミュレーターと並行して使用するカスタムエミュレーター

npx emulate init --custom inventory を使用して、サードパーティ API エミュレーターとテストをひな形から作成します。emulate の defineConfig を使って、emulate.config.ts に { emulator: "resend" } などの組み込みエントリーと並べて登録します。npx emulate start --watch を実行すると、インポートが再読み込みされ、表示された /_emulate URL でカスタム状態を確認できます。再読み込みが成功すると、実行状態はシードにリセットされます。既存のフラット形式のシード設定も引き続き使用できます。--config で明示的なファイルを指定します。サードパーティ API エミュレーターの作成とテストについては、https://emulate.dev/docs/custom-emulators を参照してください。

コメント