TypeScript Realtime Package
A powerful and flexible realtime communication library for TypeScript that provides multiple drivers (Pusher, Socket.IO, and Bun WebSocket) for handling realtime events and broadcasting.
Installation
# Using bun
bun add @stacksjs/realtime
# Using npm
npm install @stacksjs/realtime
# Using yarn
yarn add @stacksjs/realtime
# Using pnpm
pnpm add @stacksjs/realtime
Basic Usage
import { broadcast, channel } from '@stacksjs/realtime'
// Broadcasting an event
const orderData = {
orderId: '12345',
userId: 'user_123',
products: [
{ id: 'prod_1', name: 'Widget', quantity: 2 },
{ id: 'prod_2', name: 'Gadget', quantity: 1 },
],
totalAmount: 99.99,
shippingAddress: {
street: '123 Main St',
city: 'Anytown',
country: 'USA',
postalCode: '12345',
},
}
// Trigger the broadcast
await broadcast('OrderShipped', orderData)
Creating Broadcast Events
Create a broadcast event by defining a handler in your broadcasts directory:
// app/Broadcasts/OrderShipped.ts
import type { RealtimeOptions } from '@stacksjs/types'
import { channel } from '@stacksjs/realtime'
interface OrderData {
orderId: string
userId: string
products: Array<{
id: string
name: string
quantity: number
}>
totalAmount: number
shippingAddress: {
street: string
city: string
country: string
postalCode: string
}
shippedAt?: string
}
export default {
/**
_ The event name.
_/
event: 'OrderShipped',
/**
_ Handle the broadcast event.
_ This method is called when the event is triggered.
*/
async handle(data: OrderData): Promise<void> {
await channel(`orders.${data.orderId}`).private(this.event, data)
},
} satisfies RealtimeOptions
Channel Types
The package supports three types of channels:
-
Public Channels
_ Accessible to all clients _ No authentication required
- Example:
channel('notifications').public(event, data)
- Example:
-
Private Channels
_ Require authentication _ Prefixed with
private-- Example:
channel('user-123').private(event, data)
- Example:
-
Presence Channels
_ Require authentication _ Support user presence features _ Prefixed with
presence-_ Example:channel('chat-room').presence(event, data)
Broadcasting Methods
Basic Broadcasting
// Broadcast to a public channel
await channel('notifications').public('new-message', { message: 'Hello!' })
// Broadcast to a private channel
await channel('user-123').private('status-update', { status: 'online' })
// Broadcast to a presence channel
await channel('chat-room').presence('user-joined', { user: 'John' })
Broadcasting with Events
// Define your event data interface
interface MessageData {
content: string
sender: string
timestamp: string
}
// Create a broadcast event
// app/Broadcasts/NewMessage.ts
export default {
event: 'NewMessage',
async handle(data: MessageData): Promise<void> {
await channel(`chat.${data.sender}`).private(this.event, data)
},
} satisfies RealtimeOptions
// Trigger the broadcast
await broadcast('NewMessage', {
content: 'Hello!',
sender: 'user_123',
timestamp: new Date().toISOString(),
})
Configuration
The package can be configured through the application's configuration file:
// config/realtime.ts
export default {
driver: 'pusher', // or 'socket' or 'bun'
// Pusher configuration
pusher: {
appId: 'your-app-id',
key: 'your-key',
secret: 'your-secret',
cluster: 'mt1',
useTLS: true,
},
// Socket.IO configuration
socket: {
host: 'localhost',
port: 3000,
},
}
Best Practices
-
Channel Naming
_ Use descriptive names for channels _ Follow the naming convention:
{type}.{identifier}- Example:
orders.12345,chat.user_123
- Example:
-
Event Naming
_ Use PascalCase for event names _ Be specific and descriptive
- Example:
OrderShipped,MessageReceived
- Example:
-
Type Safety
_ Define interfaces for your event data _ Use TypeScript to ensure type safety
- Export types for reuse across your application
-
Security
_ Use private channels for sensitive data _ Implement proper authentication for private and presence channels
- Validate data before broadcasting
-
Organization
_ Keep broadcast events in a dedicated directory _ Group related events together
- Use consistent naming conventions