fastify-xemail
fastify-xemail
Fastify 5 plugin for email via Postmark or SendGrid (legacy). Decorate your server with fastify.xEmail and send transactional, template, attachment, and bulk emails through one API. SendGrid additionally supports address validation and Marketing contacts/lists.
Migration note: SendGrid support is legacy. New integrations should use
provider: 'postmark'. Existing SendGrid registrations keep working unchanged (default remains'sendgrid').
1.3.0. Postmark provider (provider: 'postmark') for send / sendTemplate / sendWithAttachments / sendBulk / sendPersonalizedBulk; provider option ('sendgrid' | 'postmark', default 'sendgrid'); messageStream extra option for Postmark; SendGrid-only methods (validate, contacts, lists) throw under postmark. SHA 4561c766.
Installation
```bash
npm install @xenterprises/fastify-xemail fastify@5
fastify@^5.0.0 is a peer dependency. TypeScript declarations ship with the package.
Quick Start (Postmark)
import Fastify from "fastify";
import xEmail from "@xenterprises/fastify-xemail";
const fastify = Fastify();
await fastify.register(xEmail, {
provider: "postmark",
apiKey: process.env.POSTMARK_SERVER_TOKEN,
fromEmail: "noreply@example.com",
fromName: "My App",
});
fastify.post("/welcome", async (request) => {
return fastify.xEmail.send(
request.body.email,
"Welcome!",
"<h1>Welcome!</h1><p>Thanks for signing up.</p>"
);
});
await fastify.listen({ port: 3000 });
Legacy SendGrid registration (default provider) omits provider or sets provider: 'sendgrid' and passes SENDGRID_API_KEY / SENDGRID_FROM_EMAIL.
The plugin reads no environment variables. Your app reads process.env.POSTMARK_SERVER_TOKEN (or the SendGrid vars) and passes values into register().
Providers
| Provider | Status | Sending methods | validate, contacts, lists |
|---|---|---|---|
postmark | Migration target | send, sendTemplate, sendWithAttachments, sendBulk, sendPersonalizedBulk | Throw [xEmail] '<method>()' is not supported by provider 'postmark'. |
sendgrid | Legacy (default) | All sending methods | Fully supported |
Postmark has no equivalents for email validation or contact/list management, so those methods exist on the decorator under Postmark but throw.
Options
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
provider | 'sendgrid' | 'postmark' | 'sendgrid' | No | Email provider. sendgrid is legacy; postmark is the migration target. |
apiKey | string | - | Yes | Postmark server API token, or SendGrid API key (Mail Send; Marketing APIs for contact/list methods). |
fromEmail | string | - | Yes | Verified sender email address. |
fromName | string | - | No | Sender display name. |
active | boolean | true | No | Set false to skip plugin registration entirely (no decorator is added). |
Invalid options fail fast at registration, e.g.:
xemail: option `apiKey` must be a string, e.g. `app.register(xEmail, { apiKey: 'SG.your-api-key' })`
Methods
Sending
- send - Send a transactional email with HTML content.
- send-template - Send using a provider template (SendGrid dynamic template ID or Postmark alias/ID).
- send-with-attachments - Send an email with file attachments.
- send-bulk - Send the same email to multiple recipients.
- send-personalized-bulk - Send different content to each recipient.
Under Postmark, extraOptions on send/sendTemplate accepts replyTo, cc, bcc, headers, and messageStream (mapped to Postmark PascalCase fields).
Validation (SendGrid only)
- validate - Validate an email address using the SendGrid Email Validation API. Throws under
provider: 'postmark'.
Contacts (SendGrid only)
- contacts-add - Add or update a contact in SendGrid Marketing.
- contacts-search - Search for a marketing contact by email address.
- contacts-delete - Delete a marketing contact by ID.
Lists (SendGrid only)
- lists-create - Create a new SendGrid Marketing contact list.
- lists-get - Get all contact lists.
- lists-delete - Delete a contact list by ID.
Error Reference
Startup Errors
| Error | Cause |
|---|---|
xemail: option `apiKey` must be a string, e.g. `app.register(xEmail, { apiKey: 'SG.your-api-key' })` | Missing or non-string apiKey at registration. |
xemail: option `fromEmail` must be a string, e.g. `app.register(xEmail, { apiKey: 'SG.your-api-key', fromEmail: 'noreply@example.com' })` | Missing or non-string fromEmail at registration. |
xemail: option `fromName` must be a string, e.g. `app.register(xEmail, { apiKey: 'SG.your-api-key', fromEmail: 'noreply@example.com', fromName: 'My App' })` | fromName provided but not a string. |
xemail: option `active` must be a boolean | active provided but not a boolean. |
xemail: option `provider` must be 'sendgrid' or 'postmark' | Invalid provider at registration. |
Runtime Errors
All runtime errors are thrown with the [xEmail] prefix. See each method page for the specific error messages. Under provider: 'postmark', SendGrid-only methods throw [xEmail] '<method>()' is not supported by provider 'postmark'.
Environment Variables
The plugin never reads process.env. These variables are a consumer-side convention: your application reads them and passes the values into app.register(xEmail, { ... }). Nothing here is "required" by the plugin itself - only the apiKey and fromEmail options are required (see Options above).
| Variable | Maps to option | Description |
|---|---|---|
POSTMARK_SERVER_TOKEN | apiKey | Postmark server API token (when provider: 'postmark'). |
SENDGRID_API_KEY | apiKey | SendGrid API key with Mail Send and Marketing permissions (legacy). |
SENDGRID_FROM_EMAIL | fromEmail | Verified sender email address (legacy SendGrid convention). |
SENDGRID_FROM_NAME | fromName (optional) | Sender display name. |
How It Works
The plugin validates registration options and selects a provider module: src/providers/postmark.js or src/providers/sendgrid.js. SendGrid uses @sendgrid/mail (transactional) and @sendgrid/client (Marketing and Validation REST APIs). Postmark uses the postmark package ServerClient. Both decorate the Fastify instance with fastify.xEmail. SendGrid-only methods remain on the decorator under Postmark but throw. Errors are caught, logged via fastify.log.error (credentials are never logged), and re-thrown with a [xEmail] prefix. validate() returns a soft error result instead of throwing (SendGrid only). Setting active: false skips registration entirely.
AI Context
package: "@xenterprises/fastify-xemail"
version: 1.3.0
type: fastify-plugin
use-when: Transactional email via Postmark (preferred) or SendGrid (legacy); templates, bulk, attachments; SendGrid-only validate/contacts/lists
decorator: fastify.xEmail
methods: send, sendTemplate, sendWithAttachments, sendBulk, sendPersonalizedBulk, validate (SendGrid), contacts (add/search/delete, SendGrid), lists (create/get/delete, SendGrid)
opts: provider (sendgrid|postmark, default sendgrid), apiKey, fromEmail, fromName, active; extraOptions.messageStream (Postmark)
env: POSTMARK_SERVER_TOKEN, SENDGRID_API_KEY, SENDGRID_FROM_EMAIL, SENDGRID_FROM_NAME (optional) - consumer-set only
