Email Backends¶
litestar-email provides multiple email backends for different use cases.
Most applications should use EmailService via
DI or EmailConfig.provide_service() and treat direct backend access as an
advanced/testing concern.
Available Backends¶
Backend |
Config Class |
Use Case |
|---|---|---|
|
None |
Development (prints to stdout) |
|
None |
Testing (stores in memory) |
|
|
Production SMTP servers |
|
|
Resend API (modern hosting) |
|
|
SendGrid API (enterprise) |
|
|
Mailgun API (transactional) |
Note
API backends (Resend, SendGrid, Mailgun) use httpx which is bundled with
Litestar. No extra installation is needed. Optionally, you can use aiohttp
as an alternative transport by installing litestar-email[aiohttp].
SMTP Backend¶
The SMTP backend uses aiosmtplib for async email delivery. It supports STARTTLS, implicit SSL, and authentication.
SMTP Installation¶
pip install litestar-email[smtp]
SMTP Configuration¶
from litestar_email import EmailConfig, SMTPConfig
# Basic SMTP (no encryption, no auth)
config = EmailConfig(
backend=SMTPConfig(host="localhost", port=25),
from_email="noreply@example.com",
)
# SMTP with STARTTLS (port 587)
config = EmailConfig(
backend=SMTPConfig(
host="smtp.example.com",
port=587,
username="user@example.com",
password="your-password",
use_tls=True, # STARTTLS
),
from_email="noreply@example.com",
)
# SMTP with implicit SSL (port 465)
config = EmailConfig(
backend=SMTPConfig(
host="smtp.example.com",
port=465,
username="user@example.com",
password="your-password",
use_ssl=True, # Implicit SSL
),
from_email="noreply@example.com",
)
SMTPConfig Options¶
Option |
Type |
Default |
Description |
|---|---|---|---|
|
str |
localhost | SMTP server hostname |
|
|
int |
25 |
SMTP server port |
|
str|None |
None |
Authentication username |
|
str|None |
None |
Authentication password |
|
bool |
False |
Enable STARTTLS after connecting |
|
bool |
False |
Use implicit SSL/TLS (port 465) |
|
int |
30 |
Connection timeout in seconds |
Resend Backend¶
The Resend backend sends emails via Resend’s HTTP API. This is ideal for modern hosting platforms that block SMTP ports.
Note
No extra installation needed. httpx is bundled with Litestar.
Resend Configuration¶
from litestar_email import EmailConfig, ResendConfig
config = EmailConfig(
backend=ResendConfig(api_key="re_xxxxxxxxxxxxxxxxxxxxxxxxxx"),
from_email="noreply@yourdomain.com",
)
# With aiohttp transport (requires litestar-email[aiohttp])
config = EmailConfig(
backend=ResendConfig(
api_key="re_xxxxxxxxxxxxxxxxxxxxxxxxxx",
http_transport="aiohttp",
),
from_email="noreply@yourdomain.com",
)
Get your API key at: https://resend.com/api-keys
ResendConfig Options¶
Option |
Type |
Default |
Description |
|---|---|---|---|
|
str |
“” |
Resend API key (re_xxx) |
|
int |
30 |
HTTP request timeout |
|
str |
“httpx” |
HTTP transport (“httpx”, “aiohttp”) |
SendGrid Backend¶
The SendGrid backend sends emails via SendGrid’s v3 API. This is suitable for enterprise email delivery at scale.
Note
No extra installation needed. httpx is bundled with Litestar.
SendGrid Configuration¶
from litestar_email import EmailConfig, SendGridConfig
config = EmailConfig(
backend=SendGridConfig(api_key="SG.xxxxxxxxxxxxxxxxxxxxxxxxxx"),
from_email="noreply@yourdomain.com",
)
Get your API key at: https://app.sendgrid.com/settings/api_keys
SendGridConfig Options¶
Option |
Type |
Default |
Description |
|---|---|---|---|
|
str |
“” |
SendGrid API key (SG.xxx) |
|
int |
30 |
HTTP request timeout |
|
str |
“httpx” |
HTTP transport (“httpx”, “aiohttp”) |
Mailgun Backend¶
The Mailgun backend sends emails via Mailgun’s HTTP API. Mailgun is a popular transactional email service with good deliverability.
Note
No extra installation needed. httpx is bundled with Litestar.
Mailgun Configuration¶
from litestar_email import EmailConfig, MailgunConfig
# US region (default)
config = EmailConfig(
backend=MailgunConfig(
api_key="key-xxxxxxxxxxxxxxxxxxxxxxxxxx",
domain="mg.yourdomain.com",
),
from_email="noreply@yourdomain.com",
)
# EU region
config = EmailConfig(
backend=MailgunConfig(
api_key="key-xxxxxxxxxxxxxxxxxxxxxxxxxx",
domain="mg.yourdomain.com",
region="eu",
),
from_email="noreply@yourdomain.com",
)
Get your API key at: https://app.mailgun.com/settings/api_security
MailgunConfig Options¶
Option |
Type |
Default |
Description |
|---|---|---|---|
|
str |
“” |
Mailgun API key (key-xxx) |
|
str |
“” |
Mailgun sending domain |
|
str |
“us” |
API region (“us” or “eu”) |
|
int |
30 |
HTTP request timeout |
|
str |
“httpx” |
HTTP transport (“httpx”, “aiohttp”) |
Error Handling¶
All backends raise consistent exceptions for error handling:
from litestar_email.exceptions import (
EmailBackendError, # Configuration/initialization errors
EmailConnectionError, # Connection failures
EmailAuthenticationError, # Auth failures
EmailDeliveryError, # Sending failures
EmailRateLimitError, # API rate limiting
)
try:
await backend.send_messages([message])
except EmailRateLimitError as e:
# Wait and retry
await asyncio.sleep(e.retry_after or 60)
except EmailDeliveryError as e:
# Log and handle delivery failure
logger.error(f"Email delivery failed: {e}")
except EmailConnectionError as e:
# Handle connection issues
logger.error(f"Cannot connect to email server: {e}")
Fail Silently¶
All backends support a fail_silently option that suppresses exceptions:
config = EmailConfig(
backend=SMTPConfig(host="localhost", port=1025),
fail_silently=True, # Suppress sending errors
)
backend = config.get_backend() # uses config.fail_silently
Connection Pooling¶
All backends support the async context manager protocol for connection pooling:
backend = get_backend("smtp", config=config)
# Connection is opened and closed per call
await backend.send_messages([message1])
await backend.send_messages([message2])
# Better: Reuse connection for multiple sends
async with backend:
await backend.send_messages([message1])
await backend.send_messages([message2])
await backend.send_messages([message3])
Custom Backends¶
You can implement your own backend by subclassing BaseEmailBackend and registering it:
from litestar_email.backends import BaseEmailBackend, register_backend
from litestar_email.exceptions import EmailDeliveryError
@register_backend("mybackend")
class MyBackend(BaseEmailBackend):
__slots__ = ("_client",)
async def open(self) -> bool:
if self._client is not None:
return False
self._client = await create_client()
return True
async def close(self) -> None:
if self._client is not None:
await self._client.aclose()
self._client = None
async def send_messages(self, messages: list["EmailMessage"]) -> int:
if not messages:
return 0
new_connection = await self.open()
try:
sent = 0
for message in messages:
try:
await self._send_message(message)
sent += 1
except Exception as exc:
if not self.fail_silently:
raise EmailDeliveryError("Failed to send email") from exc
return sent
finally:
if new_connection:
await self.close()
Use config.get_backend("mybackend") once it is registered, or use the import path
"your_module.MyBackend" directly without registration.
Optional Dependencies¶
If your backend uses an optional dependency, use the MissingDependencyError pattern:
from litestar_email.exceptions import MissingDependencyError
from litestar_email.utils.module_loader import _require_dependency
class MyBackend(BaseEmailBackend):
def __init__(self, ...) -> None:
# Raises MissingDependencyError with install instructions if missing
_require_dependency("mypackage", install_package="mybackend")
super().__init__(...)
Contributing a Backend (PR)¶
When submitting a backend to this repo, include:
Implementation under
src/litestar_email/backends/with__slots__and Google-style docstrings.Optional dependency guards (see above) and new extras in
pyproject.toml.Registration in
src/litestar_email/backends/__init__.pyif it is a built-in backend.Tests covering success + failure paths (mock external APIs where needed).
Documentation updates (this page + README examples if applicable).
Quality checks:
make testandmake lintmust pass90%+ coverage on new modules
No
from __future__ import annotationsand noOptional[T](useT | None)