← All @molecule/* packages · App templates

@molecule/api-video-rooms

Core interface · video-rooms · API (Node) · v1.0.1 · Apache-2.0

Real-time video rooms core interface — room lifecycle, signed meeting tokens, and cloud recordings — with swappable provider bonds (Daily.co default; LiveKit, Twilio Video, Agora alternates). Used by virtual-classroom, telemedicine, video-conferencing, and screen-sharing apps.

npm install @molecule/api-video-rooms

npm · Source on GitHub

How it works

@molecule/api-video-rooms is the video-rooms core interface on the API (Node) side: the API your app calls, with no vendor inside.

Choose the implementation by bonding one of its 2 providers: @molecule/api-video-rooms-daily-co, @molecule/api-video-rooms-livekit.

import {
  setProvider,
  createRoom,
  createMeetingToken,
  listRecordings,
} from '@molecule/api-video-rooms'
import { createProvider } from '@molecule/api-video-rooms-daily-co'

// Bond a provider at startup (reads DAILY_CO_API_KEY when config is omitted)
setProvider(createProvider())

// Create a room
const room = await createRoom({
  name: 'class-101',
  privacy: 'private',
  maxParticipants: 30,
  recording: true,
})

// Issue a join token for a student
const token = await createMeetingToken(room.name, {
  userName: 'Ada',
  expiresAt: new Date(Date.now() + 60 * 60_000),
})

// After the meeting, list recordings
const recordings = await listRecordings(room.name)

Providers (2): @molecule/api-video-rooms-daily-co, @molecule/api-video-rooms-livekit

Works with: @molecule/api-bond, @molecule/api-i18n

Reference

Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit src/index.ts JSDoc, not this file.

Video rooms core interface for molecule.dev.

Defines the standard interface for real-time video room providers (Daily.co, LiveKit, Twilio Video, Agora, etc.). Used by apps such as virtual classrooms, telemedicine, video conferencing, and screen sharing.

Quick Start

import {
  setProvider,
  createRoom,
  createMeetingToken,
  listRecordings,
} from '@molecule/api-video-rooms'
import { createProvider } from '@molecule/api-video-rooms-daily-co'

// Bond a provider at startup (reads DAILY_CO_API_KEY when config is omitted)
setProvider(createProvider())

// Create a room
const room = await createRoom({
  name: 'class-101',
  privacy: 'private',
  maxParticipants: 30,
  recording: true,
})

// Issue a join token for a student
const token = await createMeetingToken(room.name, {
  userName: 'Ada',
  expiresAt: new Date(Date.now() + 60 * 60_000),
})

// After the meeting, list recordings
const recordings = await listRecordings(room.name)

Type

core

Installation

npm install @molecule/api-video-rooms @molecule/api-bond @molecule/api-i18n

API

Interfaces

CreateMeetingTokenOptions

Options for issuing a meeting token (signed join credential).

interface CreateMeetingTokenOptions {
  /** Whether the token grants owner / moderator privileges. */
  isOwner?: boolean

  /** Display name to use for the participant when joining the room. */
  userName?: string

  /** Token expiry. After this point the token cannot be used to join. */
  expiresAt?: Date
}

CreateRoomOptions

Options for creating a new video room.

interface CreateRoomOptions {
  /** Room name. If omitted, the provider may auto-generate a unique name. */
  name?: string

  /** When the room should expire and stop accepting joins. */
  expiresAt?: Date

  /** Maximum simultaneous participants (provider-dependent cap may apply). */
  maxParticipants?: number

  /** Whether cloud recording should be enabled for this room. */
  recording?: boolean

  /** Visibility/privacy of the room. Defaults to `public` when omitted. */
  privacy?: RoomPrivacy
}

Recording

A normalised description of a cloud recording produced by a room.

interface Recording {
  /** Provider-stable recording identifier. */
  id: string

  /** Name of the room the recording belongs to. */
  roomName: string

  /** When the recording started. */
  startedAt?: Date

  /** Recording duration in seconds, when known. */
  duration?: number

  /** Provider's reported processing/availability status. */
  status?: 'processing' | 'ready' | 'failed' | 'deleted'

  /** Time-limited download URL for the recording, when available. */
  downloadUrl?: string
}

Room

A normalised description of an existing room as returned by a provider.

interface Room {
  /** Provider-stable room name / identifier. */
  name: string

  /** Joinable URL for the room. */
  url: string

  /** When the room expires, if a TTL is configured. */
  expiresAt?: Date

  /** Maximum simultaneous participants, if configured. */
  maxParticipants?: number

  /** Whether cloud recording is enabled on this room. */
  recording?: boolean

  /** Visibility/privacy of the room. */
  privacy?: RoomPrivacy
}

RoomCreated

Result of creating a new video room.

Extends {@link Room} with an optional pre-issued owner meeting token so callers can hand a single payload to clients without a follow-up call.

interface RoomCreated extends Room {
  /** Optional meeting token for the room creator (owner privileges). */
  token?: string
}

VideoRoomsProvider

Video rooms provider interface.

All video-rooms providers must implement this interface to provide a normalised surface for room lifecycle, signed join tokens and cloud recordings.

interface VideoRoomsProvider {
  /**
   * Creates a new video room.
   *
   * @param options - Room creation options.
   * @returns The created room, including its joinable URL.
   */
  createRoom(options: CreateRoomOptions): Promise<RoomCreated>

  /**
   * Deletes an existing room by name. Idempotent: deleting a non-existent
   * room must not throw.
   *
   * @param name - The room name / identifier.
   */
  deleteRoom(name: string): Promise<void>

  /**
   * Retrieves an existing room by name.
   *
   * @param name - The room name / identifier.
   * @returns The room if it exists, otherwise `null`.
   */
  getRoom(name: string): Promise<Room | null>

  /**
   * Issues a signed meeting token (join credential) for a room.
   *
   * @param roomName - The room the token is scoped to.
   * @param options - Token options (owner flag, display name, expiry).
   * @returns The signed token string.
   */
  createMeetingToken(roomName: string, options?: CreateMeetingTokenOptions): Promise<string>

  /**
   * Lists cloud recordings produced by a room.
   *
   * @param roomName - The room to list recordings for.
   * @returns The list of recordings, possibly empty.
   */
  listRecordings(roomName: string): Promise<Recording[]>
}

Types

RoomPrivacy

Privacy level for a video room.

  • public — anyone with the room URL can join.
  • private — joiners must present a meeting token issued by {@link VideoRoomsProvider.createMeetingToken}.
type RoomPrivacy = 'public' | 'private'

Functions

createMeetingToken(roomName, options)

Issues a signed meeting token (join credential) for a room using the bonded provider.

function createMeetingToken(roomName: string, options?: CreateMeetingTokenOptions): Promise<string>
  • roomName — The room the token is scoped to.
  • options — Token options (owner flag, display name, expiry).

Returns: The signed token string.

createRoom(options)

Creates a new video room using the bonded provider.

function createRoom(options?: CreateRoomOptions): Promise<RoomCreated>
  • options — Room creation options.

Returns: The created room, including its joinable URL.

deleteRoom(name)

Deletes an existing room by name using the bonded provider.

function deleteRoom(name: string): Promise<void>
  • name — The room name / identifier.

getProvider()

Retrieves the bonded video rooms provider, throwing if none is configured.

function getProvider(): VideoRoomsProvider

Returns: The bonded video rooms provider.

getRoom(name)

Retrieves an existing room by name using the bonded provider.

function getRoom(name: string): Promise<Room | null>
  • name — The room name / identifier.

Returns: The room if it exists, otherwise null.

hasProvider()

Checks whether a video rooms provider is currently bonded.

function hasProvider(): boolean

Returns: true if a video rooms provider is bonded.

listRecordings(roomName)

Lists cloud recordings produced by a room using the bonded provider.

function listRecordings(roomName: string): Promise<Recording[]>
  • roomName — The room to list recordings for.

Returns: The list of recordings, possibly empty.

setProvider(provider)

Registers a video rooms provider as the active singleton. Called by bond packages during application startup.

function setProvider(provider: VideoRoomsProvider): void
  • provider — The video rooms provider implementation to bond.

Available Providers

ProviderPackage
Video Rooms@molecule/api-video-rooms-daily-co
Video Rooms@molecule/api-video-rooms-livekit

Injection Notes

Requirements

Peer dependencies:

  • @molecule/api-bond ^1.0.1
  • @molecule/api-i18n ^1.0.1

Runtime Dependencies

  • @molecule/api-bond

  • @molecule/api-i18n

  • Rooms are PUBLIC by default. privacy defaults to 'public' when omitted — anyone with the room URL can join. For anything user-scoped, create the room with privacy: 'private' and mint short-lived per-user join tokens via createMeetingToken(roomName, { userName, expiresAt }).

  • Server-side only. The provider API key (for the bundled Daily.co bond: DAILY_CO_API_KEY) stays in the API's env. The browser receives ONLY the join token / room URL your endpoint returns — never the key, and never direct provider API calls.

  • Recording.downloadUrl is time-limited — download/persist promptly (e.g. via the uploads package); a stored URL will dead-link later.

  • Ad-hoc rooms ≠ scheduled meetings: for calendar-style events with a start time and a stable invite link use @molecule/api-video-meetings instead.

E2E Tests

Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip. CAVEAT: the live A/V call and the in-room video UI run inside the provider's client and CANNOT be driven in the sandbox — verify the room LIFECYCLE and the per-participant join TOKENS you mint server-side, never the media itself:

  • Creating a room returns a usable handle: createRoom(...) yields a RoomCreated with a stable name and a joinable url, and the app persists that name on its own record — not a throwaway URL it can never resolve again.
  • Each participant gets its OWN token: the app calls createMeetingToken(room.name, { userName, expiresAt, isOwner }) per user, so two joiners receive two DISTINCT, short-lived credentials — never one static shared secret reused for everyone.
  • The token honors role + scope + expiry: an owner/moderator token (isOwner: true) differs from a plain-participant token, each is scoped to the single room.name it was minted for (it admits no other room), and it carries the requested expiresAt — inspect the minted token's claims; don't assume it, and don't hand an owner token to an ordinary participant.
  • getRoom(name) reflects real state: a created room resolves with its configured privacy/maxParticipants/recording, and after deleteRoom(name) it returns null — ending a room actually removes it, so its old URL/tokens no longer admit a join. (Live participant count is NOT in the core Room type — don't assert on it.)
  • Size/quota holds: if the app sets maxParticipants, the created room carries that cap (the provider enforces it at join) — it isn't silently dropped.
  • SECURITY — the provider API key (e.g. DAILY_CO_API_KEY) stays server-side; the browser only ever receives a token/URL your endpoint returned, never the key or a direct provider call. Private rooms are un-guessable: only an authorized user's request mints a token, and no unauthenticated caller joins a private room by guessing its name/URL without one.