
Security News
Deno 2.2 Improves Dependency Management and Expands Node.js Compatibility
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.
Postal.js is an in-memory message bus - very loosely inspired by AMQP - written in JavaScript. Postal.js runs in the browser, or on the server-side using Node.js. It takes the familiar "eventing-style" paradigm (of which most JavaScript developers are familiar) and extends it by providing "broker" and subscriber implementations which are more sophisticated than what you typically find in simple event delegation.
Using a local message bus can enable to you de-couple your web application's components in a way not possible with other 'eventing' approaches. In addition, strategically adopting messaging at the 'seams' of your application (e.g. - between modules, at entry/exit points for browser data and storage) can not only help enforce better overall architectural design, but also insulate you from the risks of tightly coupling your application to 3rd party libraries. For example:
SubscriptionDefinition
object.Postal.js is in good company - there are many options for <airquotes>pub/sub</airquotes> in the browser. However, I grew frustrated with most of them because they often closely followed an event-delegation-paradigm, instead of providing a structured in-memory message bus. Central to postal.js are four concepts:
A channel is a logical partition of topics. Conceptually, it's like a dedicated highway for a specific set of communication. At first glance it might seem like that's overkill for an environment that runs in an event loop, but it actually proves to be quite useful. Every library has architectural opinions that it either imposes or nudges you toward. Channel-oriented messaging nudges you to separate your communication by bounded context, and enables the kind of fine-tuned visibility you need into the interactions between components as your application grows.
In my experience, seeing publish and subscribe calls all over application logic is usually a strong code smell. Ideally, the majority of message-bus integration should be concealed within application infrastructure. Having a hierarchical-wildcard-bindable topic system makes it very easy to keep things concise (especially subscribe calls!). For example, if you have a module that needs to listen to every message published on the ShoppingCart channel, you'd simply subscribe to "#", and never have to worry about additional subscribes on that channel again - even if you add new messages in the future. If you need to capture all messages with ".validation" at the end of the topic, you'd simply subscribe to "#.validation". If you needed to target all messages with topics that started with "Customer.", ended with ".validation" and had only one period-delimited segment in between, you'd subscribe to "Customer.*.validation" (thus your subscription would capture Customer.address.validation and Customer.email.validation").
Here are four examples of using Postal. All of these examples - AND MORE! - can run live here. (Please bear in mind this fiddle is pulling the postal lib from github, so running these in IE will not work due to the mime type mismatch.) Be sure to check out the wiki for API documentation and conceptual walk-throughs.
// This gets you a handle to the default postal channel...
// For grins, you can get a named channel instead like this:
// var channel = postal.channel( 'DoctorWho' );
var channel = postal.channel();
// subscribe to 'name.change' topics
var subscription = channel.subscribe( "name.change", function ( data ) {
$( "#example1" ).html( "Name: " + data.name );
} );
// And someone publishes a name change:
channel.publish( "name.change", { name : "Dr. Who" } );
// To unsubscribe, you:
subscription.unsubscribe();
// postal also provides a top-level ability to subscribe/publish
// used primarily when you don't need to hang onto a channel instance:
var anotherSub = postal.subscribe({
channel : "MyChannel",
topic : "name.change",
callback : function(data, envelope) {
$( "#example1" ).html( "Name: " + data.name );
}
});
postal.publish({
channel : "MyChannel",
topic : "name.change",
data : {
name : "Dr. Who"
}
});
The *
symbol represents "one word" in a topic (i.e - the text between two periods of a topic). By subscribing to "*.changed"
, the binding will match name.changed
& location.changed
but not changed.companion
.
var chgSubscription = channel.subscribe( "*.changed", function ( data ) {
$( "<li>" + data.type + " changed: " + data.value + "</li>" ).appendTo( "#example2" );
} );
channel.publish( "name.changed", { type : "Name", value : "John Smith" } );
channel.publish( "location.changed", { type : "Location", value : "Early 20th Century England" } );
chgSubscription.unsubscribe();
The #
symbol represents 0-n number of characters/words in a topic string. By subscribing to "DrWho.#.Changed"
, the binding will match DrWho.NinthDoctor.Companion.Changed
& DrWho.Location.Changed
but not Changed
.
var starSubscription = channel.subscribe( "DrWho.#.Changed", function ( data ) {
$( "<li>" + data.type + " Changed: " + data.value + "</li>" ).appendTo( "#example3" );
} );
channel.publish( "DrWho.NinthDoctor.Companion.Changed", { type : "Companion Name", value : "Rose" } );
channel.publish( "DrWho.TenthDoctor.Companion.Changed", { type : "Companion Name", value : "Martha" } );
channel.publish( "DrWho.Eleventh.Companion.Changed", { type : "Companion Name", value : "Amy" } );
channel.publish( "DrWho.Location.Changed", { type : "Location", value : "The Library" } );
channel.publish( "TheMaster.DrumBeat.Changed", { type : "DrumBeat", value : "This won't trigger any subscriptions" } );
channel.publish( "Changed", { type : "Useless", value : "This won't trigger any subscriptions either" } );
starSubscription.unsubscribe();
var dupChannel = postal.channel( "Blink" ),
dupSubscription = dupChannel.subscribe( "WeepingAngel.#", function( data ) {
$( '<li>' + data.value + '</li>' ).appendTo( "#example4" );
}).distinctUntilChanged();
// demonstrating multiple channels per topic being used
// You can do it this way if you like, but the example above has nicer syntax (and *much* less overhead)
dupChannel.publish( "WeepingAngel.DontBlink", { value:"Don't Blink" } );
dupChannel.publish( "WeepingAngel.DontBlink", { value:"Don't Blink" } );
dupChannel.publish( "WeepingAngel.DontEvenBlink", { value:"Don't Even Blink" } );
dupChannel.publish( "WeepingAngel.DontBlink", { value:"Don't Close Your Eyes" } );
dupChannel.publish( "WeepingAngel.DontBlink", { value:"Don't Blink" } );
dupChannel.publish( "WeepingAngel.DontBlink", { value:"Don't Blink" } );
dupSubscription.unsubscribe();
Please visit the postal.js wiki for API documentation, discussion of concepts and links to blogs/articles on postal.js.
There are four main ways you can extend Postal:
subscribe
, publish
and addWiretap
calls all simply wrap a concrete implementation provided by the postal.configuration.bus
object. For example, if you wanted a bus that stored message history in local storage and pushed a dump of past messages to a new subscriber, you'd simply write your implementation and then swap the default one out by calling: postal.configuration.bus = myWayBetterBusImplementation
.bindingResolver
matches subscriptions to message topics being published. You may not care for the AMQP-style bindings functionality. No problem! Write your own resolver object that implements a compare
and reset
method and swap the core version out with your implementation by calling: postal.configuration.resolver = myWayBetterResolver
.It's also possible to extend the monitoring of messages passing through Postal by adding a "wire tap". A wire tap is a callback that will get invoked for any published message (even if no actual subscriptions would bind to the message's topic). Wire taps should not be used in lieu of an actual subscription - but instead should be used for diagnostics, logging, forwarding (to a websocket publisher or a local storage wrapper, for example) or other concerns that fall along those lines. This repository used to include a console logging wiretap called postal.diagnostics.js - you can now find it here in it's own repo. This diagnostics wiretap can be configured with filters to limit the firehose of message data to specific channels/topics and more.
Please - by all means! While I hope the API is relatively stable, I'm open to pull requests. (Hint - if you want a feature implemented, a pull request gives it a much higher probability of being included than simply asking me.) As I said, pull requests are most certainly welcome - but please include tests for your additions. Otherwise, it will disappear into the ether.
Here's where Postal is headed:
SubscriptionDefinition
object will be given the ability to pause (skip) responding to subscriptionsFAQs
Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.
The npm package postal receives a total of 12,286 weekly downloads. As such, postal popularity was classified as popular.
We found that postal demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
Security News
React's CRA deprecation announcement sparked community criticism over framework recommendations, leading to quick updates acknowledging build tools like Vite as valid alternatives.
Security News
Ransomware payment rates hit an all-time low in 2024 as law enforcement crackdowns, stronger defenses, and shifting policies make attacks riskier and less profitable.