
Security News
Risky Biz Podcast: Making Reachability Analysis Work in Real-World Codebases
This episode explores the hard problem of reachability analysis, from static analysis limits to handling dynamic languages and massive dependency trees.
@boringnode/transmit
Advanced tools
@boringnode/transmit
is a framework-agnostic opinionated library to manage Server-Sent Events (SSE) in Node.js.
Here are a few things you should know before using this module.
👉 Unidirectional Communication: The data transmission occurs only from server to client, not the other way around.
👉 Textual Data Only: SSE only supports the transmission of textual data, binary data cannot be sent.
👉 HTTP Protocol: The underlying protocol used is the regular HTTP, not any special or proprietary protocol.
npm install @boringnode/transmit
This module is designed to be used with any HTTP server framework. If you wish to write an adapter for a specific framework, please refer to the Adapters section for examples.
Once the connection is established, you can send data to the client using the transmit.broadcast
method.
// Given the "transmit" instance from the adapter
transmit.broadcast('global', { message: 'Hello' })
transmit.broadcast('chats/1/messages', { message: 'Hello' })
transmit.broadcast('users/1', { message: 'Hello' })
You can authorize the client to subscribe to a specific channel by using the authorize
function. In the following example, we are using the AdonisJS Framework.
import transmit from '@adonisjs/transmit/services/main'
import Chat from '#models/chat'
import type { HttpContext } from '@adonisjs/core/http'
transmit.authorize<{ id: string }>('users/:id', (ctx: HttpContext, { id }) => {
return ctx.auth.user?.id === +id
})
transmit.authorize<{ id: string }>('chats/:id/messages', async (ctx: HttpContext, { id }) => {
const chat = await Chat.findOrFail(+id)
return ctx.bouncer.allows('accessChat', chat)
})
By default, broadcasting events works only within the context of an HTTP request. However, you can broadcast events from the background using the transmit service if you register a transport in your configuration.
The transport layer is responsible for syncing events across multiple servers or instances. It works by broadcasting any events (like broadcasted events, subscriptions, and un-subscriptions) to all connected servers or instances using a Message Bus.
The server or instance responsible for your client connection will receive the event and broadcast it to the client.
import { Transmit } from '@boringnode/transmit'
import { redis } from '@boringnode/transmit/transports'
const transmit = new Transmit({
transport: {
driver: redis({
host: process.env.REDIS_HOST,
port: process.env.REDIS_PORT,
password: process.env.REDIS_PASSWORD,
keyPrefix: 'transmit',
})
}
})
You can listen for events on the client-side using the @adonisjs/transmit-client
package. The package provides a Transmit
class. The client use the EventSource API by default to connect to the server.
[!NOTE] Even if you are not working with AdonisJS, you can still use the
@adonisjs/transmit-client
package.
import { Transmit } from '@adonisjs/transmit-client'
export const transmit = new Transmit({
baseUrl: window.location.origin
})
const subscription = transmit.subscription('chats/1/messages')
await subscription.create()
subscription.onMessage((data) => {
console.log(data)
})
subscription.onMessageOnce(() => {
console.log('I will be called only once')
})
const stopListening = subscription.onMessage((data) => {
console.log(data)
})
// Stop listening
stopListening()
await subscription.delete()
Here are the available adapters for specific frameworks:
To write an adapter for a specific framework, you need to implement the following routes:
GET /__transmit/events
: This route is used to establish a connection between the client and the server. It returns a stream that will be used to send data to the client.POST /__transmit/subscribe
: This route is used to subscribe the client to a specific channel.POST /__transmit/unsubscribe
: This route is used to unsubscribe the client from a specific channel.Here is an example of how you can implement the adapter for fastify
:
import Fastify from 'fastify'
import { Transmit } from '@boringnode/transmit'
const fastify = Fastify({
logger: true
})
const transmit = new Transmit({
pingInterval: false,
transport: null
})
/**
* Register the client connection and keep it alive.
*/
fastify.get('__transmit/events', (request, reply) => {
const uid = request.query.uid as string
if (!uid) {
return reply.code(400).send({ error: 'Missing uid' })
}
const stream = transmit.createStream({
uid,
context: { request, reply }
request: request.raw,
response: reply.raw,
injectResponseHeaders: reply.getHeaders()
})
return reply.send(stream)
})
/**
* Subscribe the client to a specific channel.
*/
fastify.post('__transmit/subscribe', async (request, reply) => {
const uid = request.body.uid as string
const channel = request.body.channel as string
const success = await transmit.subscribe({
uid,
channel,
context: { request, reply }
})
if (!success) {
return reply.code(400).send({ error: 'Unable to subscribe to the channel' })
}
return reply.code(204).send()
})
/**
* Unsubscribe the client from a specific channel.
*/
fastify.post('__transmit/unsubscribe', async (request, reply) => {
const uid = request.body.uid as string
const channel = request.body.channel as string
const success = await transmit.unsubscribe({
uid,
channel,
context: { request, reply }
})
if (!success) {
return reply.code(400).send({ error: 'Unable to unsubscribe to the channel' })
}
return reply.code(204).send()
})
fastify.listen({ port: 3000 })
When deploying applications that use @boringnode/transmit
, it’s important to ensure that GZip compression does not interfere with the text/event-stream
content type used by Server-Sent Events (SSE). Compression applied to text/event-stream
can cause connection issues, leading to frequent disconnects or SSE failures.
If your deployment uses a reverse proxy (such as Traefik or Nginx) or other middleware that applies GZip, ensure that compression is disabled for the text/event-stream
content type.
traefik.http.middlewares.gzip.compress=true
traefik.http.middlewares.gzip.compress.excludedcontenttypes=text/event-stream
traefik.http.routers.my-router.middlewares=gzip
FAQs
A framework agnostic Server-Sent-Event library
The npm package @boringnode/transmit receives a total of 3,233 weekly downloads. As such, @boringnode/transmit popularity was classified as popular.
We found that @boringnode/transmit demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 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
This episode explores the hard problem of reachability analysis, from static analysis limits to handling dynamic languages and massive dependency trees.
Security News
/Research
Malicious Nx npm versions stole secrets and wallet info using AI CLI tools; Socket’s AI scanner detected the supply chain attack and flagged the malware.
Security News
CISA’s 2025 draft SBOM guidance adds new fields like hashes, licenses, and tool metadata to make software inventories more actionable.