Skip to content

steve-lebleu/cliam

Repository files navigation

Cliam

Node Bun TypeScript

Github action workflow status GitHub Release GPL Licence

Maintainability Code Coverage

Transactional emails with a kick

Agnostic transactional email sending in Node.js environment

> Why ?

To improve and facilitate the implementation, the flexibility and the maintenance of transactional emailing tasks.

> Features

  • Agnostic transactional email sending using web API or SMTP server. One input, one output.
  • Multiple simultaneous transporters.
  • Configuration based, not implementation based: easy switch between different modes.
  • Normalized transactions events.
  • Securized payloads.
  • Customisable default templates.

> Table of contents

> Requirements

  • Node.js >= 18.19.0
  • NPM >= 10.2.3

> Getting started

Install

> npm i cliam --save

Configure

Cliam must be configured once at application startup, before any call to mail(). Two approaches are available.

Option A - Pass a configuration object directly:

import { Cliam } from 'cliam';
import type { IClientConfiguration } from 'cliam';

const config: IClientConfiguration = {
  sandbox: true,
  variables: {
    addresses: {
      from: { name: 'My App', email: 'no-reply@my-website.com' },
      replyTo: { name: 'My App', email: 'no-reply@my-website.com' }
    }
  },
  transporters: [
    {
      id: 'smtp-main',
      auth: {
        username: process.env.SMTP_USERNAME,
        password: process.env.SMTP_PASSWORD
      },
      options: {
        host: process.env.SMTP_HOST,
        port: 587,
        secure: false
      }
    },
    {
      id: 'sendgrid-api',
      provider: 'sendgrid',
      auth: {
        apiKey: process.env.SENDGRID_API_KEY
      },
      templates: {
        'user.welcome': 'd-321bb40f548e4db8a628b4d6464ebacc'
      }
    }
  ]
};

Cliam.configure(config);

Option B - Load from a .cliamrc.js file:

import { Cliam } from 'cliam';

// Loads .cliamrc.js from process.cwd() by default
Cliam.configureFromFile();

// Or pass an explicit path
Cliam.configureFromFile('/path/to/my-cliam-config.js');

The .cliamrc.js file should export the same configuration object as above:

require('dotenv').config();

module.exports = {
  sandbox: true,
  variables: {
    addresses: {
      from: { name: 'My App', email: 'no-reply@my-website.com' },
      replyTo: { name: 'My App', email: 'no-reply@my-website.com' }
    }
  },
  transporters: [ ... ]
};

Use environment variables for sensitive values such as API keys. Cliam does not load .env automatically.

See cliamrc configuration wiki section for the full list of available options.

Selective imports

The default entry point registers all supported providers at import time:

import { Cliam } from 'cliam'; // all providers loaded

If you use a bundler and want to reduce your bundle size, import the core and load only the providers you actually use:

import { Cliam } from 'cliam/core';
import 'cliam/providers/sendgrid';
// import 'cliam/providers/smtp'; // add others as needed

Types and enums are available separately:

import type { IPayload, IClientConfiguration } from 'cliam/types';
import { EVENT, PROVIDER } from 'cliam/types';

Available sub-paths:

Sub-path Exports
cliam Cliam, SendingResponse, SendingError, all types - registers all providers
cliam/core Cliam, SendingResponse, SendingError- no providers registered
cliam/types All interfaces, enums and types - no runtime code
cliam/providers/brevo Side-effect: registers the Brevo transporter
cliam/providers/mailersend Side-effect: registers the Mailersend transporter
cliam/providers/mailgun Side-effect: registers the Mailgun transporter
cliam/providers/mailjet Side-effect: registers the Mailjet transporter
cliam/providers/mandrill Side-effect: registers the Mandrill transporter
cliam/providers/postmark Side-effect: registers the Postmark transporter
cliam/providers/resend Side-effect: registers the Resend transporter
cliam/providers/sendgrid Side-effect: registers the Sendgrid transporter
cliam/providers/ses Side-effect: registers the Amazon SES transporter
cliam/providers/sparkpost Side-effect: registers the Sparkpost transporter
cliam/providers/smtp Side-effect: registers the SMTP transporter

If a transporter is configured but its provider was never imported, Cliam throws at send time with a clear message.

Implement

import { Cliam } from 'cliam';
import type { IPayload } from 'cliam';

const payload: IPayload = {
  meta: {
    from: { email: 'john.doe@hotmail.com', name: 'John Doe' },
    to: [{ email: 'john.allan.poe@hotmail.com' }],
    replyTo: { email: 'john.doe@hotmail.com', name: 'John Doe' },
    subject: 'Welcome John'
  },
  content: [
    {
      type: 'text/html',
      value: '<h1>Hello Yoda</h1><p>I use Cliam to send my emails !</p>'
    }
  ]
};

Cliam.mail('user.welcome', payload)
  .then(res => {
    console.log('Email delivered: ', res);
  })
  .catch(err => {
    console.log('Error: ', err);
  });

The render engine is inferred automatically:

  • content provided → uses your HTML directly
  • No content, template ID configured for the event → delegates to the provider's template engine
  • No content, no template → uses Cliam's built-in default templates

By default, Cliam uses the first transporter in the configuration. Pass transporterId in the payload to target a specific one.

See email payload wiki section for the full payload reference.

> Beneficiary use cases

✅ I have many projects which uses differents providers, it's a hell of a thing to maintain.

This is to be forgotten with Cliam. No more worries about polymorphics inputs / outputs. Whether you are working with an A, B, C, D provider or a smtp server, your input / output will always be the same regardless of your delivery method or service provider.

✅ I'm just using many providers into a same project, it's convoluted to securize and maintain.

Same purpose. Whether you are working with a provider or a SMTP server - or boths, with Cliam, you don't get anymore n libraries into your node_modules folder: we only embed the bare metal minimum, and not some old deprecated SDK's.

✅ I wish change from supplier, but I'm afraid about the implementation ?

Without Cliam, indeed you could be. With, your implementation does not move, you just have to adapt a configuration when you want to change your method. Remove your legacy code, and embrace simplicity through Cliam.

✅ I don't have a subscription to a supplier, and no templates

No problem, we have all been poor once. Start with a simple SMTP server and use default templates. When your business is up, you can use a paid web api.

✅ I have a big problem with a provider, and my emails stay blocked in the pipe !

The same: fallback on a SMTP server or another provider. In two minutes you're ready and your mailing is back in operation.

> Supported web API providers

Resend Sparkpost
Sendgrid Postmark
Mailersend Mailgun
Mailjet Brevo
Mandrill Amazon SES

> Licence

AGPL-3.0 License

Sponsor this project

Packages

 
 
 

Contributors