Getting Started Guide

tests codecov license npm npm

Cloud Native Notifications Library

Airhorn makes it easy to send SMS, SMTP, Webhooks, and mobile push notifications easily using templates through your standard cloud providers. We focused on making it cloud native by default (using cloud services).

Features

  • Supports multiple notification types: SMS, Email, Mobile Push, Webhooks
  • A unified API for all notification types using the send() method.
  • Powerful Hooks system (BeforeSend, AfterSend) for intercepting and modifying messages with full parameter access.
  • Event Emitting built in by default for extendability and observability.
  • Send Strategy (Round Robin, Fail Over, All) Choose the best delivery method for each notification.
  • Built in Webhook support for sending notifications to external services.
  • Built in support for retries and error handling on sends.
  • Advanced caching on template compilation and execution.
  • Load a template from a file for easy GitOps based workflows.
  • Many supported providers such as Twilio (with Sendgrid), AWS, and Google Cloud.
  • Robust (6+ template formats) templating via ecto
  • Easily build your own provider with minimal effort via AirhornProvider interface.
  • Statistics tracking for send successes, failures, and execution times (instance only).
  • ESM and Typescript based supporting Nodejs 20+
  • Maintained on a regular basis with updates and improvements.

Getting Started

To get started with Airhorn, you can install the package via npm:

npm install airhorn @airhornjs/twilio
    
import { Airhorn, AirhornProviderType } from "airhorn";
    import { AirhornTwilio } from "@airhornjs/twilio";
    
    const providers = [
    	new AirhornTwilio({
    		accountSid: "your_account_sid",
    		authToken: "your_auth_token"
    	}),
    ];
    
    const airhorn = new Airhorn({
    	providers
    });
    
    // this will give you twilio and webhook (built in) support. Now lets create a template and send it!
    const template = {
    	from: "+12223334444",
    	content: "Hey <%= name %> this is a test message from Airhorn",
    	templateEngine: "ejs",
    }
    
    const data = { name: "John" };
    
    await airhorn.send("+1234567890", template, data, AirhornProviderType.SMS);
    

Check out the documentation and providers to learn more!

Hooks

Airhorn provides a powerful hook system that allows you to intercept and modify notifications before and after they are sent. This enables custom validation, logging, transformation, and post-processing of messages.

Available Hooks

  • BeforeSend: Called after template rendering but before the message is sent to providers
  • AfterSend: Called after the message has been sent and providers have responded

BeforeSend Hook

The BeforeSend hook receives the rendered message and options, allowing you to modify them before sending:

import { Airhorn, AirhornHook } from "airhorn";
    
    const airhorn = new Airhorn();
    
    // Add a hook to modify message content before sending
    airhorn.addHook(AirhornHook.BeforeSend, ({ message, options }) => {
    	// Add a prefix to all messages
    	message.content = `[IMPORTANT] ${message.content}`;
    
    	// Modify the recipient
    	if (message.to.includes("test")) {
    		message.to = "[email protected]";
    	}
    
    	// Log the outgoing message
    	console.log("Sending message:", message);
    });
    

AfterSend Hook

The AfterSend hook receives the complete result object after sending, allowing you to post-process or enrich the result:

// Add a hook to track results after sending
    airhorn.addHook(AirhornHook.AfterSend, ({ result }) => {
    	// Add custom metadata
    	result.metadata = {
    		processedAt: new Date(),
    		environment: process.env.NODE_ENV
    	};
    
    	// Send to analytics
    	if (result.success) {
    		analytics.track("notification_sent", {
    			type: result.message?.type,
    			provider: result.providers[0]?.name
    		});
    	}
    
    	// Log failures
    	if (!result.success) {
    		logger.error("Notification failed", result.errors);
    	}
    });
    

Multiple Hooks

You can register multiple hooks of the same type, and they will execute in the order they were registered:

// First hook: validation
    airhorn.addHook(AirhornHook.BeforeSend, ({ message }) => {
    	if (!message.content || message.content.length === 0) {
    		throw new Error("Message content cannot be empty");
    	}
    });
    
    // Second hook: sanitization
    airhorn.addHook(AirhornHook.BeforeSend, ({ message }) => {
    	message.content = sanitizeHtml(message.content);
    });
    
    // Third hook: logging
    airhorn.addHook(AirhornHook.BeforeSend, ({ message }) => {
    	logger.info("Sending notification", { to: message.to, type: message.type });
    });
    

Hook Error Handling

By default, hook errors are caught and handled gracefully. You can configure Airhorn to throw hook errors by setting throwOnErrors:

const airhorn = new Airhorn({ throwOnErrors: true });
    
    airhorn.addHook(AirhornHook.BeforeSend, ({ message }) => {
    	// This error will now throw and stop execution
    	throw new Error("Validation failed");
    });
    

Providers

We currently support multiple providers and you can easily add more by following the AirhornProvider interface. Here are the supported providers:

We currently support twilio, aws, and azure with thier offerings. Here is a chart showing what functionality is in each:

Provider SMS Email Push Webhook
(built in airhorn)
@airhornjs/twilio
@airhornjs/aws
@airhornjs/azure

Note: We used to support firebase because of mobile push but it made more sense to focus on aws and azure because it is more comprehensive.

How to Contribute

Now that you've set up your workspace, you're ready to contribute changes to the airhorn repository you can refer to the CONTRIBUTING guide. If you have any questions please feel free to ask by creating an issue and label it question.

Licensing and Copyright

This project is MIT License © Jared Wray