Send emails with Node.js
Use @flaresend/client to send email from any Node.js app.
Prerequisites
- A deployed mailer and its URL, for example
https://mailer.example.com. See Deploy. - An API key for your project in
FLARESEND_API_KEY. See API keys. - Node.js 18 or later (the client uses the global
fetch).
Install
npm install @flaresend/clientThe package ships both ES modules and CommonJS, so import and require both work.
Send an email
import { Flaresend } from '@flaresend/client';
const flaresend = new Flaresend({
apiKey: process.env.FLARESEND_API_KEY!,
baseUrl: 'https://mailer.example.com',
});
const { id, status } = await flaresend.emails.send({
from: 'Acme <hello@acme.com>',
to: ['ada@example.com', 'grace@example.com'],
subject: 'Your weekly report',
html: '<p>Here is your report.</p>',
text: 'Here is your report.',
tags: { kind: 'weekly-report' },
});
console.log(id, status); // email_01K6B2Y4ZP9R3M7T8V5N2QXW4C queuedCreate one client and reuse it. It holds no connection, only your key, base URL and retry settings.
Send with a template
The mailer has built-in templates such as welcome, password-reset and magic-link. Pass template and data, and the subject comes from the template:
await flaresend.emails.send({
from: 'Acme <hello@acme.com>',
to: 'ada@example.com',
template: 'welcome',
data: { name: 'Ada', appName: 'Acme', loginUrl: 'https://acme.com/login' },
});See Built-in templates.
Idempotency
Every emails.send call already carries a random idempotency key, so the client's own retries after a timeout never send twice. To stop the same email going out twice across separate calls (a double click, a job that runs twice), pass your own key:
await flaresend.emails.send(
{ from: 'Acme <hello@acme.com>', to: user.email, template: 'welcome', data },
{ idempotencyKey: `welcome-${user.id}` },
);A second call with the same key and the same content returns the first email with idempotent: true. The same key with different content fails with 409 idempotency_payload_mismatch. See Idempotency.
Handle errors
Every failed call throws a FlaresendError:
import { Flaresend, FlaresendError } from '@flaresend/client';
try {
await flaresend.emails.send({ from: 'Acme <hello@acme.com>', to: 'ada@example.com', subject: 'Hi', text: 'Hi' });
} catch (err) {
if (err instanceof FlaresendError) {
console.error(err.status, err.type, err.code, err.message, err.param);
// 422 unprocessable recipient_suppressed "ada@example.com is on the suppression list (hard_bounce)" ada@example.com
}
throw err;
}The fields come straight from the API's error body:
{
"error": {
"type": "unprocessable",
"code": "recipient_suppressed",
"message": "ada@example.com is on the suppression list (hard_bounce)",
"param": "ada@example.com"
}
}The client retries 429, 5xx and network errors up to 2 times by default (maxRetries). It never retries 429 daily_limit_exceeded. See Errors.
Next steps
- HTTP client reference: every method on
Flaresend. - Attachments:
attachmentFromBytesencodes files for you. - Webhooks: get delivery, bounce and complaint events.