EMAIL_TRANSPORT looks like it selects the email transport, but it only
recognises one of the three that exist. The rest are chosen by side conditions.
Current behaviour
makeSendEmail (server/services/sendEmailService/sendEmailService.ts) falls
through in this order:
- an SES client was injected → SES
EMAIL_TRANSPORT=console → console
SENDGRID_API_KEY is set → SendGrid
- otherwise → SES via
new SESClient({})
So setting an API key silently changes which transport you use, and there is no
value of EMAIL_TRANSPORT that selects SES or SendGrid deliberately.
Problems
The console transport needs a guard to be safe. It logs recipient addresses
and rendered notification content to stdout, so it throws unless
NODE_ENV=development. That guard is the tell: it is a debugging hack occupying
the slot that should hold a proper fake.
There is no local development story for email. You either print to the
console or send real mail. Nothing renders an actual message for inspection.
Email sending is untraced. All three transports go through vendor SDKs, and
per #1238 those are not instrumented under ESM anyway, so adding OpenTelemetry
coverage today would require solving that first.
Proposal
- Make
EMAIL_TRANSPORT a real selector — ses, sendgrid, smtp — with the
transport map alongside it, matching how @adonisjs/mail shapes this
(default plus a mailers map) and how config/database.ts,
config/redis.ts and config/dataWarehouse.ts now shape connections.
- Add an SMTP transport and a Mailpit service to
docker-compose.yaml, so
local development sees real rendered email in a UI.
- Replace the console transport with a fake/mock transport, and delete its
NODE_ENV guard along with it.
- Optionally consolidate: one library behind all transports instead of three
vendor SDKs (@aws-sdk/client-ses, @sendgrid/mail, plus whatever provides
SMTP). Both Upyo (MIT, scoped @upyo/* packages per
transport, includes mock and opentelemetry) and
nodemailer would serve; either is fine.
server/config/email.ts already exists as of #1235, so the reads are in one
place and this change is only about how the transport is chosen.
Gates
Adding a mail library and a Mailpit compose service both require human approval
per AGENTS.md — a dependency licence/CVE check, and docker-compose.yaml is
on the approval list. Worth noting Upyo is pre-1.0 (0.6.0), which matters more
than usual given customers self-host.
EMAIL_TRANSPORTlooks like it selects the email transport, but it onlyrecognises one of the three that exist. The rest are chosen by side conditions.
Current behaviour
makeSendEmail(server/services/sendEmailService/sendEmailService.ts) fallsthrough in this order:
EMAIL_TRANSPORT=console→ consoleSENDGRID_API_KEYis set → SendGridnew SESClient({})So setting an API key silently changes which transport you use, and there is no
value of
EMAIL_TRANSPORTthat selects SES or SendGrid deliberately.Problems
The console transport needs a guard to be safe. It logs recipient addresses
and rendered notification content to stdout, so it throws unless
NODE_ENV=development. That guard is the tell: it is a debugging hack occupyingthe slot that should hold a proper fake.
There is no local development story for email. You either print to the
console or send real mail. Nothing renders an actual message for inspection.
Email sending is untraced. All three transports go through vendor SDKs, and
per #1238 those are not instrumented under ESM anyway, so adding OpenTelemetry
coverage today would require solving that first.
Proposal
EMAIL_TRANSPORTa real selector —ses,sendgrid,smtp— with thetransport map alongside it, matching how
@adonisjs/mailshapes this(
defaultplus amailersmap) and howconfig/database.ts,config/redis.tsandconfig/dataWarehouse.tsnow shape connections.docker-compose.yaml, solocal development sees real rendered email in a UI.
NODE_ENVguard along with it.vendor SDKs (
@aws-sdk/client-ses,@sendgrid/mail, plus whatever providesSMTP). Both Upyo (MIT, scoped
@upyo/*packages pertransport, includes
mockandopentelemetry) andnodemailer would serve; either is fine.
server/config/email.tsalready exists as of #1235, so the reads are in oneplace and this change is only about how the transport is chosen.
Gates
Adding a mail library and a Mailpit compose service both require human approval
per
AGENTS.md— a dependency licence/CVE check, anddocker-compose.yamlison the approval list. Worth noting Upyo is pre-1.0 (
0.6.0), which matters morethan usual given customers self-host.