Tutorial
Enviar e-mails com Node.js
Envie e-mails do Node.js com o SDK @emailit/node ou com o Nodemailer por SMTP, e verifique os webhooks do Emailit em uma rota do Express.
Este guia mostra como enviar e-mails de uma aplicação Node.js com o SDK oficial @emailit/node, como usar o Nodemailer por SMTP como alternativa e como receber webhooks assinados no Express.
Pré-requisitos
- Node.js 18 ou mais recente.
- Um domínio de envio verificado, por exemplo,
acme.com. - Uma chave de API. Uma chave Sending Only é suficiente para enviar.
- Até o seu workspace ter acesso de produção, você só pode enviar para os e-mails das contas dos membros do workspace. Use o seu próprio endereço durante os testes.
Instalar o SDK
npm install @emailit/nodeO pacote é apenas ESM. Use a sintaxe import ou await import('@emailit/node') em código CommonJS.
Configurar a chave de API
Mantenha a chave fora do seu código. Coloque-a em uma variável de ambiente:
EMAILIT_API_KEY=secret_••••••••••••••••••••••••••••••••Carregue o arquivo com node --env-file=.env (Node.js 20.6 e mais recentes), com um pacote como o dotenv ou com as configurações de segredos da sua plataforma. Adicione o .env ao .gitignore.
Enviar um e-mail
Crie um único cliente e reutilize-o entre as requisições:
import { Emailit } from '@emailit/node';
const emailit = new Emailit(process.env.EMAILIT_API_KEY);
const email = await emailit.emails.send({
from: 'Acme <hello@acme.com>',
to: 'ada@example.com',
subject: 'Welcome to Acme',
html: '<p>Thanks for signing up.</p>',
text: 'Thanks for signing up.',
});
console.log(email.id); // em_…Execute-o com node --env-file=.env send.mjs. O e-mail aparece em Email APIEmails em poucos segundos.
Em uma aplicação Express, envie a partir da rota que dispara o e-mail:
import express from 'express';
import { Emailit } from '@emailit/node';
const app = express();
const emailit = new Emailit(process.env.EMAILIT_API_KEY);
app.post('/signup', express.json(), async (req, res) => {
const { email } = req.body;
// Create the user here, then send the welcome email.
const sent = await emailit.emails.send({
from: 'Acme <hello@acme.com>',
to: email,
template: 'welcome',
variables: { email },
});
res.status(201).json({ emailId: sent.id });
});
app.listen(3000);template recebe o alias ou o ID tem_ de um template, e variables o preenche. Consulte Templates e a lista completa de campos em Enviar um e-mail.
Tratar erros
O SDK lança erros tipados, então você pode reagir a cada falha:
import {
AuthenticationException,
RateLimitException,
UnprocessableEntityException,
ApiErrorException,
} from '@emailit/node';
try {
await emailit.emails.send(message);
} catch (err) {
if (err instanceof RateLimitException) {
// 429: wait err.jsonBody.retry_after seconds, then retry
} else if (err instanceof AuthenticationException) {
// 401: the API key is missing or invalid
} else if (err instanceof UnprocessableEntityException) {
// 422: for example, the from domain isn't verified
} else if (err instanceof ApiErrorException) {
console.error(err.httpStatus, err.jsonBody);
} else {
throw err;
}
}Enviar por SMTP como alternativa
Se a sua aplicação já usa o Nodemailer, aponte-o para o SMTP relay do Emailit. Instale-o com npm install nodemailer:
import nodemailer from 'nodemailer';
const transporter = nodemailer.createTransport({
host: 'smtp.emailit.com',
port: 587,
secure: false, // upgraded with STARTTLS after connecting
requireTLS: true,
auth: {
user: 'emailit',
pass: process.env.EMAILIT_API_KEY,
},
});
const info = await transporter.sendMail({
from: 'Acme <hello@acme.com>',
to: 'ada@example.com',
subject: 'Welcome to Acme',
text: 'Thanks for signing up.',
html: '<p>Thanks for signing up.</p>',
});
console.log(info.response); // 250 2.0.0 OK: queued as em_…Para a porta 465, defina port: 465 e secure: true. Se a sua rede bloquear a 587, use 2525 ou 2587 com as mesmas configurações. Consulte Configurações de SMTP.
Receber webhooks
Crie um webhook que aponte para https://your-app.com/webhooks/emailit e copie o segredo de assinatura dele (whsec_…) para EMAILIT_WEBHOOK_SECRET.
O Emailit assina cada requisição com um HMAC-SHA256 de timestamp.rawBody, então a rota precisa ler o corpo bruto. Use express.raw() nesta rota, não express.json():
import express from 'express';
import { createHmac, timingSafeEqual } from 'node:crypto';
function verifyEmailitSignature(rawBody, signature, timestamp, secret) {
if (!signature || !timestamp) return false;
const age = Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp));
if (!Number.isFinite(age) || age > 300) return false; // reject replays older than 5 minutes
const expected = createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(signature);
return a.length === b.length && timingSafeEqual(a, b);
}
export const webhooks = express.Router();
webhooks.post('/webhooks/emailit', express.raw({ type: 'application/json' }), (req, res) => {
const rawBody = req.body.toString('utf8');
const valid = verifyEmailitSignature(
rawBody,
req.get('x-emailit-signature'),
req.get('x-emailit-timestamp'),
process.env.EMAILIT_WEBHOOK_SECRET,
);
if (!valid) return res.status(401).send('Invalid signature');
// The body is an array of up to 100 events.
const events = JSON.parse(rawBody);
for (const event of events) {
switch (event.type) {
case 'email.delivered':
// event.data.object.id is the em_ ID
break;
case 'email.bounced':
case 'email.complained':
// stop emailing event.data.object.to
break;
}
}
res.sendStatus(200);
});Monte o router com app.use(webhooks) antes de qualquer middleware global express.json(). Retorne um 2xx em até 30 segundos; qualquer outra resposta recebe novas tentativas. Leia Assinatura das requisições e Novas tentativas e falhas para mais detalhes.
Dicas para produção
- Restrinja o escopo da chave. Use no servidor da aplicação uma chave Sending Only restrita ao seu domínio de envio. Reserve as chaves Full Access para scripts administrativos.
- Fique dentro do seu limite de envio. Por padrão, os workspaces novos podem enviar 2 e-mails por segundo e 5.000 por dia, compartilhados entre a API e o SMTP. Envie os jobs em massa a partir de uma fila. Com o Nodemailer,
pool: true, rateLimit: 2mantém um transporte com pool em 2 mensagens por segundo. Consulte Limites. - Torne as novas tentativas seguras. Se você tentar um envio de novo depois de um timeout, envie um cabeçalho
Idempotency-Keypara que o Emailit não envie duas vezes. O SDK não define cabeçalhos personalizados, então usefetchnessas chamadas; consulte Idempotência. - Processe os webhooks de forma idempotente. Guarde cada
event_idque você já tratou e ignore os duplicados, e faça o trabalho demorado em um job em segundo plano depois de retornar200. - Usa TypeScript? O SDK ainda não tem declarações de tipos. Adicione
declare module '@emailit/node';a um arquivo.d.tsdo seu projeto.