@molecule/api-emails-sendgrid
v1.0.1
Published
SendGrid email provider for molecule.dev.
Readme
@molecule/api-emails-sendgrid
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
SendGrid email provider for molecule.dev.
Quick Start
import { setTransport } from '@molecule/api-emails'
import { provider } from '@molecule/api-emails-sendgrid'
setTransport(provider)Type
provider
Installation
npm install @molecule/api-emails-sendgrid @molecule/api-bond @molecule/api-emails @molecule/api-secrets @sendgrid/mailAPI
Interfaces
EmailMessage
Email message options.
interface EmailMessage {
/**
* Sender address.
*/
from: string | EmailAddress
/**
* Recipient(s).
*/
to: string | EmailAddress | (string | EmailAddress)[]
/**
* CC recipient(s).
*/
cc?: string | EmailAddress | (string | EmailAddress)[]
/**
* BCC recipient(s).
*/
bcc?: string | EmailAddress | (string | EmailAddress)[]
/**
* Reply-to address.
*/
replyTo?: string | EmailAddress
/**
* Email subject.
*/
subject: string
/**
* Plain text body.
*/
text?: string
/**
* HTML body.
*/
html?: string
/**
* File attachments.
*/
attachments?: EmailAttachment[]
/**
* i18n key for the subject (for client-side translation).
*/
subjectKey?: string
/**
* i18n key for the plain text body (for client-side translation).
*/
textKey?: string
/**
* i18n key for the HTML body (for client-side translation).
*/
htmlKey?: string
}EmailSendResult
Result of sending an email.
interface EmailSendResult {
/**
* Whether the email was accepted for delivery.
*/
accepted: string[]
/**
* Addresses that were rejected.
*/
rejected: string[]
/**
* Message ID from the provider.
*/
messageId?: string
/**
* Raw response from the provider.
*/
response?: string
}EmailTransport
Email transport interface.
All email providers must implement this interface.
interface EmailTransport {
/**
* Sends an email message.
* @returns The send result.
*/
sendMail(message: EmailMessage): Promise<EmailSendResult>
}Functions
getClient()
Returns the SendGrid mail client, applying configuration from the environment on FIRST USE and memoizing each setting thereafter.
Configuration is deferred to the first send — NOT module load — so an app that
resolves SENDGRID_API_KEY (and the optional SENDGRID_BASE_URL) into
process.env AFTER this module is imported (late secrets resolution via a
secrets bond) is honored: the value present at send time is the one applied.
Reading the key at import time instead froze an empty/stale key and every
request went out unauthenticated — an opaque SendGrid 401. Each env var is
applied once, the first time it is seen set, so whichever arrives late is
still picked up.
function getClient(): sgMail.MailServiceReturns: The configured @sendgrid/mail client.
sendMail(message)
Sends an email through the SendGrid API.
function sendMail(message: EmailMessage): Promise<EmailSendResult>message— The email message (to, from, subject, text/html, attachments).
Returns: Send result with accepted addresses, message ID, and status code.
Constants
emailsSendgridSecretDefinitions
Secret definitions required by the SendGrid email bond.
const emailsSendgridSecretDefinitions: SecretDefinition[]provider
The SendGrid email provider implementing the standard interface.
const provider: EmailTransportCore Interface
Implements @molecule/api-emails interface.
Bond Wiring
Setup function to register this provider with the core interface:
import { setTransport } from '@molecule/api-emails'
import { provider } from '@molecule/api-emails-sendgrid'
export function setupEmailsSendgrid(): void {
setTransport(provider)
}Injection Notes
Requirements
Peer dependencies:
@molecule/api-bond^1.0.1@molecule/api-emails^1.0.1@molecule/api-secrets^1.0.1
Environment Variables
SENDGRID_API_KEY(required) — SendGrid API key- Setup: SendGrid → Settings → API Keys → Create API Key with Mail Send permission.
- Get it here: https://app.sendgrid.com/settings/api_keys
- Example:
SG....
Runtime Dependencies
@molecule/api-bond@molecule/api-emails@molecule/api-secrets@sendgrid/mailConfiguration is lazy and env-driven:
SENDGRID_API_KEY(and the optionalSENDGRID_BASE_URL) are read on the FIRST send viagetClient()— NOT at import time — and applied once. So a key resolved intoprocess.envAFTER this module is imported (late secrets resolution via a secrets bond) is honored: the value present at send time is the one used. If the key is genuinely absent at send time,sendMail()throws a tagged config-missing error (clean 503 /config.notConfigured) namingSENDGRID_API_KEY— never an opaque SendGrid 401.SENDGRID_TEST_MODE=trueenables SendGrid sandbox mode: the API validates and accepts the message (auth + payload exercised for real) but NOTHING is delivered.SENDGRID_BASE_URL(optional, read lazily on first send too) overrides the API base URL for brokers/compatible endpoints.Stream attachments are not supported — Buffer/string content only; a stream throws. On success
acceptedechoes everytorecipient (SendGrid returns no per-recipient verdict) andmessageIdis taken from thex-message-idresponse header.
E2E Tests
Integration checklist — drive the real UI (live preview, no mocks). The
sandbox CAPTURES outbound email instead of sending — read each message with
the read_activity tool (filter type 'email'); the verification/reset link
is in its payload. Never mock the send or modify production code to expose
it. Adapt each item to this app's actual screens/flows, and check every box
off one by one. A box you can't check is an integration bug to fix — not a
skip:
- [ ] Each email-triggering flow (signup verification, password-reset request, invites/notifications the app defines) confirms the send in the UI ("check your inbox") and a message actually reaches the transport.
- [ ] The password-reset round-trip completes: request a reset → open the captured message → follow its single-use link → set a new password → log in with it (and the old password no longer works).
- [ ] The message body contains a LINK, never the raw token/secret, and renders
with the app's real name/content (no
undefinedplaceholders). - [ ] Requesting a reset for an unknown email shows the same neutral UI response as a known one (no account-existence oracle).
- [ ] Account emails go only to the account's own address — no UI or endpoint lets an unauthenticated caller send to an arbitrary address.
