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

@lattestream/server

v1.1.0

Published

LatteStream server SDK for Node.js and Deno

Readme

@lattestream/server

npm version License: MIT

LatteStream server SDK for Node.js and Deno. Trigger events, authorize channels, and manage real-time connections from your backend.

Features

  • Secure Authentication - Channel authorization with encrypted secrets
  • High Performance - Connection pooling, batching, caching, and retry logic
  • TypeScript First - Full type safety with comprehensive type definitions
  • Multi-runtime - Works with Node.js and Deno
  • Event Batching - Automatic event batching for better performance

Installation

npm install @lattestream/server

Or with yarn/pnpm:

yarn add @lattestream/server
pnpm add @lattestream/server

Quick Start

import LatteStreamServer from '@lattestream/server';

// Initialize with encrypted secret
const server = new LatteStreamServer('lsk_your_encrypted_secret', {
  cluster: 'eu1',
});

// Trigger an event
await server.trigger('my-channel', 'my-event', {
  message: 'Hello World!',
});

Authentication Keys

Use your lsk_* encrypted secret (server-side only). Never expose this in client code.

When clients subscribe to private/presence channels, they'll call your auth endpoint. Your server uses authorizeChannel() to get an lspc_* token from the LatteStream service.

Triggering Events

Single Channel

await server.trigger('chat-room', 'message', {
  user: 'Alice',
  text: 'Hello!',
});

Multiple Channels

await server.trigger(['room-1', 'room-2', 'room-3'], 'notification', { alert: 'Server maintenance in 5 minutes' });

Exclude Socket ID

Useful to exclude the sender from receiving their own event:

await server.trigger('chat', 'message', data, {
  socketId: req.body.socket_id, // This socket won't receive the event
});

Batch Events

For triggering multiple different events efficiently:

await server.triggerBatch([
  {
    channel: 'channel-1',
    name: 'event-1',
    data: { foo: 'bar' },
  },
  {
    channel: 'channel-2',
    name: 'event-2',
    data: { baz: 'qux' },
    socketId: 'exclude-this-socket',
  },
]);

Channel Authorization

How Authentication Works

  1. Client connects with lspk_* public key
  2. Client subscribes to a private/presence channel (must happen within 30 seconds)
  3. Client SDK calls your /auth endpoint with socket_id and channel_name
  4. Your server calls authorizeChannel() which requests an lspc_* token from LatteStream service
  5. LatteStream service returns the authorization token
  6. Client completes subscription with the token

Using authorizeChannel()

import LatteStreamServer from '@lattestream/server';

const server = new LatteStreamServer('lsk_your_encrypted_secret');

app.post('/auth', async (req, res) => {
  const { socket_id, channel_name } = req.body;

  // Your authorization logic
  if (!req.user) {
    return res.status(403).json({ error: 'Unauthorized' });
  }

  try {
    // For presence channels, provide user data
    const userData = channel_name.startsWith('presence-')
      ? { user_id: req.user.id, user_info: { name: req.user.name } }
      : undefined;

    const authResponse = await server.authorizeChannel(socket_id, channel_name, userData);

    res.json(authResponse);
  } catch (error) {
    res.status(403).json({ error: error.message });
  }
});

Express Middleware

For Express.js, use the convenience middleware:

import { createChannelAuthMiddleware } from '@lattestream/server';

app.post(
  '/auth',
  createChannelAuthMiddleware('lsk_your_encrypted_secret', (req) => ({
    user_id: req.user.id,
    user_info: { name: req.user.name },
  }))
);

Channel Information

Get Channel Info

const info = await server.getChannelInfo('presence-lobby', ['user_count']);
console.log(info);
// { occupied: true, userCount: 42, subscriptionCount: 42 }

List Channels

const channels = await server.getChannels('presence-', ['user_count']);
console.log(channels);
// {
//   channels: {
//     'presence-lobby': { occupied: true, userCount: 42 },
//     'presence-chat': { occupied: true, userCount: 15 }
//   }
// }

Get Presence Users

const users = await server.getUsers('presence-lobby');
console.log(users);
// { users: [{ id: 'user-1' }, { id: 'user-2' }] }

Terminate User Connections

await server.terminateUserConnections('user-123');

Webhooks

LatteStream can send webhooks to your server for events like channel occupancy changes, client connections, and more. Configure your webhook URL in the LatteStream Dashboard.

Verifying Webhook Signatures

All webhook requests include an x-lattestream-signature header that you should verify to ensure the request came from LatteStream:

import { verifyWebhookSignature, WebhookEventPayload } from '@lattestream/server';

app.post('/webhooks/lattestream', (req, res) => {
  const body = req.body;
  const signature = req.headers['x-lattestream-signature'] as string;
  const verified = verifyWebhookSignature(
    body,
    signature,
    process.env.LATTESTREAM_WEBHOOK_SECRET as string
  );

  if (!verified) {
    return res.status(401).send('Invalid signature');
  }

  // Type-safe webhook payload
  const payload: WebhookEventPayload = body;

  // Process webhook events
  payload.events.forEach((event) => {
    console.log(`${event.name} on ${event.channel}`);
  });

  res.status(200).send('OK');
});

Important: Store your webhook secret from the LatteStream Dashboard in LATTESTREAM_WEBHOOK_SECRET environment variable.

API Reference

LatteStreamServer

Constructor

new LatteStreamServer(encryptedSecret: string, options?: LatteStreamServerOptions)

Methods

Events:

  • trigger(channel, event, data, options?) - Trigger event on channel(s)
  • triggerBatch(events) - Trigger multiple events
  • flushBatch() - Manually flush batched events

Authorization:

  • authorizeChannel(socketId, channelName, userData?) - Authorize private/presence channel

Helper Functions

import {
  createChannelAuthMiddleware,
  ServerAuthorizer,
  EncryptionHelper,
  createAuthHelper,
  verifyWebhookSignature,
  WebhookEventPayload
} from '@lattestream/server';

Authentication:

  • createChannelAuthMiddleware(secret, getUserData?) - Create Express auth middleware (NOTE: this is NOT encrypted. Recommend to use authorizeChannel() instead)
  • ServerAuthorizer - Manual authorization class (advanced)
  • EncryptionHelper - Encryption utilities (advanced)
  • createAuthHelper(masterKey) - Create encryption helper (advanced)

Webhooks:

  • verifyWebhookSignature(body, signature, secret) - Verify webhook signature from LatteStream
  • WebhookEventPayload - TypeScript type for webhook request body

Configuration Options

interface LatteStreamServerOptions {
  wsEndpoint?: string; // Custom WebSocket endpoint
  cluster?: string; // Cluster region (default: 'eu1')
  useTLS?: boolean; // Use TLS (default: true)
  enableLogging?: boolean; // Enable debug logging
  timeout?: number; // Request timeout in ms (default: 30000)

  // Connection pooling
  maxConnections?: number; // Max concurrent connections (default: 20)
  connectionMaxAge?: number; // Connection max age in ms (default: 300000)

  // Caching
  cacheTimeout?: number; // Cache timeout in ms (default: 30000)

  // Retry logic
  maxRetries?: number; // Max retry attempts (default: 3)
  retryDelay?: number; // Retry delay in ms (default: 1000)

  // Event batching
  enableBatching?: boolean; // Enable event batching (default: true)
  batchSize?: number; // Max batch size (default: 50)
  batchInterval?: number; // Batch interval in ms (default: 100)
}

Performance Features

Automatic Event Batching

Events are automatically batched for better performance:

// These will be batched together
await server.trigger('channel-1', 'event', { data: 1 });
await server.trigger('channel-2', 'event', { data: 2 });
await server.trigger('channel-3', 'event', { data: 3 });

// Force immediate flush
await server.flushBatch();

Disable batching if needed:

const server = new LatteStreamServer('lsk_secret', {
  enableBatching: false,
});

Connection Pooling

Connections are automatically pooled and reused for better performance.

Request Caching

Channel info and user queries are cached automatically (30s default):

const server = new LatteStreamServer('lsk_secret', {
  cacheTimeout: 60000, // Cache for 60 seconds
});

Advanced Performance Utilities

import { ConnectionPool, BatchProcessor, RequestCache, createRetryWrapper, MemoryMonitor } from '@lattestream/server';

// Use these for custom implementations

Examples

Express.js API

import express from 'express';
import LatteStreamServer from '@lattestream/server';

const app = express();
const server = new LatteStreamServer('lsk_your_secret');

app.use(express.json());

// Trigger event endpoint
app.post('/api/message', async (req, res) => {
  try {
    await server.trigger(
      'chat',
      'message',
      {
        user: req.user.name,
        text: req.body.text,
      },
      {
        socketId: req.body.socket_id, // Exclude sender
      }
    );

    res.json({ success: true });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Auth endpoint
app.post('/auth', async (req, res) => {
  const { socket_id, channel_name } = req.body;

  const authResponse = await server.authorizeChannel(socket_id, channel_name, { user_id: req.user.id });

  res.json(authResponse);
});

app.listen(3000);

Next.js API Route

// pages/api/lattestream/trigger.js
import LatteStreamServer from '@lattestream/server';

const server = new LatteStreamServer(process.env.LATTESTREAM_SECRET);

export default async function handler(req, res) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' });
  }

  try {
    await server.trigger('notifications', 'alert', req.body);
    res.status(200).json({ success: true });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
}

Deno

import LatteStreamServer from 'npm:@lattestream/server';

const server = new LatteStreamServer(Deno.env.get('LATTESTREAM_SECRET')!);

Deno.serve(async (req) => {
  if (req.method === 'POST' && new URL(req.url).pathname === '/trigger') {
    const data = await req.json();

    await server.trigger('channel', 'event', data);

    return new Response(JSON.stringify({ success: true }), {
      headers: { 'Content-Type': 'application/json' },
    });
  }

  return new Response('Not found', { status: 404 });
});

TypeScript

This package includes TypeScript definitions out of the box.

import LatteStreamServer, {
  LatteStreamServerOptions,
  TriggerEventOptions,
  BatchTriggerEvent,
  ChannelInfo,
  WebhookEventPayload,
  verifyWebhookSignature,
} from '@lattestream/server';

const server: LatteStreamServer = new LatteStreamServer('lsk_secret', {
  cluster: 'eu1',
  enableBatching: true,
});

Environment Variables

# .env
LATTESTREAM_SECRET=lsk_your_encrypted_secret
LATTESTREAM_CLUSTER=eu1
LATTESTREAM_WEBHOOK_SECRET=your_webhook_secret_from_dashboard
const server = new LatteStreamServer(process.env.LATTESTREAM_SECRET, {
  cluster: process.env.LATTESTREAM_CLUSTER,
});

Related Packages

License

MIT License - see LICENSE for details.

Links