npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@eduardoilha/email-client

v0.1.1

Published

Typed HTTP client for the Acesse Email Service

Downloads

274

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-client

Requires 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 templates

Idempotency

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-tags

CI builds and pushes to GitHub Packages.