Skip to content

Add Resend mailer adapter #439

Description

@armanist

Summary

Resend is a modern email API service gaining significant traction among developers (4.6M+ installs of the PHP SDK). It offers a clean REST API, first-party PHP SDK (resend/resend-php), and a developer-friendly experience similar to SendGrid and Mailgun.

The Quantum Mailer package currently supports five adapters: SMTP, Mailgun, Mandrill, SendGrid, and Sendinblue. This task adds a sixth adapter -- ResendAdapter -- following the established HTTP-based adapter pattern used by the existing adapters.

Current Architecture

The mailer adapter pattern consists of:

Component Path Role
MailerInterface src/Mailer/Contracts/MailerInterface.php Contract all adapters implement
MailerTrait src/Mailer/Traits/MailerTrait.php Shared state and send() template method
MailerType src/Mailer/Enums/MailerType.php String constants for adapter keys
MailerFactory src/Mailer/Factories/MailerFactory.php ADAPTERS map + singleton factory
*Adapter src/Mailer/Adapters/ Concrete adapter classes

Each HTTP-based adapter follows this structure:

class XxxAdapter implements MailerInterface
{
    use MailerTrait;

    public string $name = 'Xxx';
    private $apiKey;
    private string $apiUrl = 'https://...';
    private array $data = [];

    public function __construct(array $params)
    {
        $this->httpClient = new HttpClient();
        $this->apiKey = $params['api_key'];
    }

    private function prepare(): void { /* map trait fields to API payload */ }
    private function sendEmail(): bool { /* HTTP POST via $this->httpClient */ }
}

Resend API Details

  • REST Endpoint: POST https://api.resend.com/emails
  • Auth: Authorization: Bearer <API_KEY>
  • Content-Type: application/json
  • PHP SDK: resend/resend-php (Packagist) -- however, the adapter should use the framework's HttpClient directly (consistent with Mailgun, SendGrid, etc.), not the Resend SDK.

Payload shape:

{
  "from": "Name <email@example.com>",
  "to": ["recipient@example.com"],
  "subject": "Hello",
  "html": "<p>Body</p>"
}

Tasks

1. Add RESEND constant to MailerType

File: src/Mailer/Enums/MailerType.php

public const RESEND = 'resend';

2. Create ResendAdapter

File: src/Mailer/Adapters/ResendAdapter.php

The adapter must:

  • implements MailerInterface and use MailerTrait
  • Set public string $name = 'Resend'
  • Accept array $params in the constructor and extract api_key
  • Initialize $this->httpClient = new HttpClient()
  • Use the Resend API endpoint https://api.resend.com/emails
  • Implement private prepare(): void -- map $this->from, $this->addresses, $this->subject, and $this->message (with template support via $this->createFromTemplate()) into the Resend payload format:
    • from as "Name <email>" string
    • to as array of email strings
    • subject as string
    • html as string (trimmed, newlines stripped)
  • Implement private sendEmail(): bool -- POST JSON to the API with Authorization: Bearer header, return true on success, false on exception (same error handling as other HTTP adapters)

3. Register the adapter in MailerFactory

File: src/Mailer/Factories/MailerFactory.php

  • Add import: use Quantum\Mailer\Adapters\ResendAdapter;
  • Add to ADAPTERS constant:
MailerType::RESEND => ResendAdapter::class,

4. Add config section for Resend

File: tests/_root/shared/config/mailer.php

'resend' => [
    'api_key' => 'resend_api_key',
],

5. Add unit tests

tests/Unit/Mailer/Adapters/ResendAdapterTest.php

Following the pattern of SendgridAdapterTest:

  • Extend MailerTestCase
  • setUp(): create new ResendAdapter(['api_key' => 'xxx111222333'])
  • testResendAdapterInstance(): assert instanceof ResendAdapter and instanceof MailerInterface
  • testResendAdapterSend(): set from/address/subject/body and assert send() returns true

tests/Unit/Mailer/Factories/MailerFactoryTest.php

Add a new test method:

public function testMailerFactoryGetResendAdapter(): void
{
    $mailer = MailerFactory::get(MailerType::RESEND);

    $this->assertInstanceOf(ResendAdapter::class, $mailer->getAdapter());
}

6. Update documentation / config stubs (if applicable)

If the project ships sample config files or documentation listing available mailer adapters, add resend to those lists.

Acceptance Criteria

  • MailerType::RESEND constant exists with value 'resend'
  • ResendAdapter class exists at src/Mailer/Adapters/ResendAdapter.php
  • ResendAdapter implements MailerInterface and uses MailerTrait
  • ResendAdapter uses the framework's HttpClient (not the Resend PHP SDK)
  • ResendAdapter sends to https://api.resend.com/emails with Bearer auth and JSON payload
  • MailerFactory::ADAPTERS includes the Resend mapping
  • Test config includes a resend section with api_key
  • ResendAdapterTest covers instantiation and send
  • MailerFactoryTest covers factory resolution for the Resend adapter
  • All existing tests continue to pass
  • Code follows the same style and conventions as the existing HTTP-based adapters

Metadata

Metadata

Fields

No fields configured for Feature.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions