Skip to content

Basic Usage ​

This guide covers the fundamental patterns for using Concorde in your application.

Registry Definition ​

The registry is the heart of Concorde. It defines all your channels and events with their schemas using the RegistryBuilder:

typescript
import { type } from 'arktype'
import { RegistryBuilder } from '@matfire/concorde'

export const registry = new RegistryBuilder()
  .channel('notifications', {
    alert: type({
      id: 'string',
      type: "'info' | 'warning' | 'error'",
      message: 'string',
      timestamp: 'number'
    }),
    'badge-update': type({
      count: 'number'
    })
  })
  .build()

Server-Side Usage ​

Creating a Server Instance ​

typescript
import Pusher from 'pusher'
import { createServer } from '@matfire/concorde/server'
import { registry } from './registry'

const pusher = new Pusher({
  appId: process.env.PUSHER_APP_ID!,
  key: process.env.PUSHER_KEY!,
  secret: process.env.PUSHER_SECRET!,
  cluster: process.env.PUSHER_CLUSTER!
})

export const server = createServer(registry, pusher)

Triggering Events ​

typescript
// Single channel
await server.trigger('notifications', 'alert', {
  id: 'alert-123',
  type: 'warning',
  message: 'System maintenance in 10 minutes',
  timestamp: Date.now()
})

// Multiple channels
await server.trigger(
  ['notifications', 'admin-notifications'], 
  'alert',
  {
    id: 'alert-456',
    type: 'error',
    message: 'Service disruption detected',
    timestamp: Date.now()
  }
)

Error Handling ​

Concorde validates data before sending it to Pusher:

typescript
try {
  await server.trigger('notifications', 'alert', {
    id: 123, // ❌ Should be string
    type: 'critical', // ❌ Not in union type
    message: 'Invalid alert',
    timestamp: Date.now()
  })
} catch (error) {
  console.error('Validation failed:', error.message)
  // "Trying to send invalid data: [validation errors]"
}

Client-Side Usage ​

Creating a Client Instance ​

typescript
import Pusher from 'pusher-js'
import { createClient } from '@matfire/concorde/client'
import { registry } from './registry'

const pusher = new Pusher(process.env.NEXT_PUBLIC_PUSHER_KEY!, {
  cluster: process.env.NEXT_PUBLIC_PUSHER_CLUSTER!
})

export const client = createClient(registry, pusher)

Subscribing to Channels ​

typescript
const channel = client.subscribe('notifications')

Binding to Events ​

typescript
// Type-safe event handler
channel.bind('alert', (alert) => {
  // alert is typed as: { id: string, type: 'info' | 'warning' | 'error', message: string, timestamp: number }
  
  switch (alert.type) {
    case 'error':
      showErrorNotification(alert.message)
      break
    case 'warning':
      showWarningNotification(alert.message)
      break
    case 'info':
      showInfoNotification(alert.message)
      break
  }
})

channel.bind('badge-update', (update) => {
  // update is typed as: { count: number }
  updateBadgeCount(update.count)
})

Multiple Event Handlers ​

typescript
const userChannel = client.subscribe('user-events')

userChannel.bind('user-joined', (data) => {
  console.log(`${data.username} joined`)
})

userChannel.bind('user-left', (data) => {
  console.log(`User ${data.userId} left`)
})

userChannel.bind('status-changed', (data) => {
  console.log(`User ${data.userId} is now ${data.status}`)
})

Unbinding Events ​

typescript
// Unbind specific event
channel.unbind('alert')

// Disconnect from all channels
client.disconnect()

Client-Side Triggering ​

Some Pusher setups allow client-side triggering:

typescript
// This will validate the data before sending
await channel.trigger('badge-update', {
  count: 5
})

Error Handling on Client ​

Invalid incoming data is automatically handled:

typescript
channel.bind('alert', (alert) => {
  // This handler will only be called with valid data
  // Invalid data is logged as a warning and ignored
})

// If the server sends invalid data, you'll see:
// "Received invalid payload [validation errors]"

Best Practices ​

  1. Keep schemas simple: Use clear, descriptive field names
  2. Use union types: For enums and limited string values
  3. Validate early: Let Concorde catch errors before they reach your handlers
  4. Share registries: Use the same registry definition across client and server
  5. Handle errors gracefully: Always wrap server triggers in try-catch blocks

Next Steps ​