@eduardoilha/email-client
v0.1.1
Published
Typed HTTP client for the Acesse Email Service
Downloads
274
Maintainers
Readme
@acesse/email-client
Typed HTTP client for the Acesse Email Service. Use it from any Node 18+ project that needs to send transactional or marketing emails through your service.
Install
Published to GitHub Packages. Configure .npmrc in the consuming repo:
@acesse:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_PACKAGES_TOKEN}Export the token before installing:
export GITHUB_PACKAGES_TOKEN=ghp_xxx # needs read:packages scope
npm install @acesse/email-clientRequires Node 18+ (uses global fetch).
Usage
import { EmailClient } from '@acesse/email-client'
const email = new EmailClient({
baseUrl: process.env.EMAIL_URL!, // e.g. http://emailservice-app:3000
apiKey: process.env.EMAIL_API_KEY!,
timeoutMs: 10_000, // optional, default 10s
})Transactional templates (seeded by the service)
The service ships with these slugs: welcome, password-reset, order-confirmed, payment-failed, email-verification.
await email.sendSystem('account-created', user.email, {
name: user.name,
ctaUrl: `${process.env.APP_URL}/verify/${token}`,
})Custom templates (DB-backed, built via the admin UI)
await email.sendCustom({
templateSlug: 'campanha-mensal',
to: '[email protected]',
data: { firstName: 'Ana', month: 'Maio' },
})Pin a specific version (for reproducible sends):
await email.sendCustom({
templateSlug: 'campanha-mensal',
to: '[email protected]',
data: { firstName: 'Ana' },
version: 3,
locale: 'pt-BR',
})Batch enqueue
const { batchId, queued, skipped } = await email.sendBatch({
templateSlug: 'campanha-mensal',
recipients: [
{ to: '[email protected]', data: { firstName: 'A' } },
{ to: '[email protected]', data: { firstName: 'B' } },
],
})Follow batch progress
const status = await email.getBatch(batchId, { errors: true })
// { status: 'completed', sent, failed, skipped, pending, errors? }Templates CRUD
const list = await email.listTemplates()
const { template, version } = await email.createTemplate({
slug: 'promo-black-friday',
name: 'Promo Black Friday',
category: 'marketing',
subject: 'Olá {{name}}, oferta especial!',
source: '<h1>Oi {{name}}</h1><p>Use cupom {{promoCode}}</p>',
variablesSchema: {
type: 'object',
required: ['name', 'promoCode'],
properties: {
name: { type: 'string' },
promoCode: { type: 'string' },
},
},
})
await email.addVersion(template.id, {
subject: 'Última chance!',
source: '<h1>Última chance, {{name}}</h1>',
variablesSchema: { /* ... */ },
activate: true,
})
await email.preview(template.id, { name: 'Test', promoCode: 'X' })
await email.deleteTemplate(template.id) // blocked on system templatesIdempotency
Pass idempotencyKey to dedupe retries on single sends:
await email.sendCustom(
{ templateSlug: 'welcome', to: '[email protected]', data: { name: 'Ana' } },
{ idempotencyKey: 'signup-user-123' },
)A second call with the same key returns the previous messageId and never hits the provider again.
Error handling
import { EmailServiceError, EmailServiceNetworkError } from '@acesse/email-client'
try {
await email.sendCustom({ templateSlug: 'welcome', to: '[email protected]' })
} catch (err) {
if (err instanceof EmailServiceError) {
console.error(err.status, err.message, err.payload)
} else if (err instanceof EmailServiceNetworkError) {
console.error('network', err.message)
} else {
throw err
}
}Examples
Next.js (App Router)
// app/api/signup/route.ts
import { EmailClient } from '@acesse/email-client'
const email = new EmailClient({
baseUrl: process.env.EMAIL_URL!,
apiKey: process.env.EMAIL_API_KEY!,
})
export async function POST(req: Request) {
const { email: addr, name } = await req.json()
await email.sendSystem('account-created', addr, {
name,
ctaUrl: `${process.env.APP_URL}/verify/${generateToken()}`,
})
return Response.json({ ok: true })
}Payment service — purchase confirmation
async function onPaymentSuccess(payment: Payment) {
await email.sendSystem('order-confirmed', payment.customerEmail, {
name: payment.customerName,
orderId: payment.orderId,
orderUrl: `${process.env.APP_URL}/orders/${payment.orderId}`,
totalAmount: formatBRL(payment.total),
itemsLabel: payment.items.map((i) => i.name).join(', '),
})
}Versioning
Semver. Patches are safe, minors add fields, majors change the API surface.
Publishing
Workflow .github/workflows/publish-client.yml runs on tags email-client-v*:
cd packages/email-client
npm version patch # bumps version + creates tag email-client-v0.1.1
git push --follow-tagsCI builds and pushes to GitHub Packages.
