MeetingRouter

TypeScript Support Guide

@meeting-baas/sdk - Client API & Bridge / typescript-support

TypeScript Support Guide

The Meeting BaaS SDK is built with TypeScript and provides comprehensive type definitions for all APIs.

Overview

The SDK offers:

  • Full type safety: All methods and parameters are fully typed
  • Type inference: Automatic type inference based on API version
  • Discriminated unions: Type-safe error handling
  • Generated types: Types generated from OpenAPI specification
  • Version-specific types: Different types for v1 and v2 APIs

Basic Type Safety

Importing Types

// Import the client factory
import { createBaasClient } from "@meeting-baas/sdk";

// Import common types
import type {
  JoinRequest,
  JoinResponse,
  CreateCalendarParams
} from "@meeting-baas/sdk";

// Import v2-specific types
import type { V2 } from "@meeting-baas/sdk";

Client Type Inference

TypeScript automatically infers the correct client type:

// v1 client type is inferred
const v1Client = createBaasClient({
  api_key: "key",
  api_version: "v1"
});

// TypeScript knows these methods are available:
v1Client.joinMeeting;      // ✅
v1Client.leaveMeeting;     // ✅
v1Client.getMeetingData;   // ✅
v1Client.createBot;        // ❌ Type error

// v2 client type is inferred
const v2Client = createBaasClient({
  api_key: "key",
  api_version: "v2"
});

// TypeScript knows these methods are available:
v2Client.createBot;        // ✅
v2Client.stopBot;          // ✅
v2Client.getBot;           // ✅
v2Client.joinMeeting;      // ❌ Type error

Request Types

v1 Request Types

import type {
  JoinRequest,
  LeaveRequest,
  GetMeetingDataParams,
  DeleteBotDataParams
} from "@meeting-baas/sdk";

const joinParams: JoinRequest = {
  meeting_url: "https://meet.google.com/abc-def-ghi",
  bot_name: "My Bot",
  reserved: true,
  // Optional parameters
  bot_image: "https://example.com/bot.jpg",
  recording_mode: "speaker_view",
  speech_to_text: {
    provider: "Gladia"
  }
};

const result = await client.joinMeeting(joinParams);

v2 Request Types

import type { V2 } from "@meeting-baas/sdk";

const createBotParams: V2.CreateBotRequest = {
  meeting_url: "https://meet.google.com/abc-def-ghi",
  bot_name: "My Bot",
  // Optional parameters
  bot_image: "https://example.com/bot.jpg",
  webhook_url: "https://example.com/webhook"
};

const result = await client.createBot(createBotParams);

Response Types

Discriminated Unions

The SDK uses discriminated unions for type-safe error handling:

// v1 response
const result = await v1Client.joinMeeting({ ... });

if (result.success) {
  // TypeScript knows result.data exists
  const botId: string = result.data.bot_id;
  // result.error is never
} else {
  // TypeScript knows result.error exists
  const error: ZodError | Error = result.error;
  // result.data is never
}
// v2 response
const result = await v2Client.createBot({ ... });

if (result.success) {
  // TypeScript knows result.data exists
  const botId: string = result.data.bot_id;
} else {
  // TypeScript knows error fields exist
  const errorMessage: string = result.error;
  const errorCode: string = result.code;
  const statusCode: number = result.statusCode;
}

Response Type Definitions

// v1 response types
type ApiResponse\<T\> =
  | { success: true; data: T; error?: never }
  | { success: false; error: ZodError | Error; data?: never };

// v2 response types
type ApiResponseV2\<T\> =
  | { success: true; data: T; error?: never }
  | {
      success: false;
      error: string;
      code: string;
      statusCode: number;
      details: unknown | null;
      data?: never;
    };

Type Narrowing

Using Type Guards

import { ZodError } from "zod";

function isZodError(error: unknown): error is ZodError {
  return error instanceof ZodError;
}

const result = await v1Client.joinMeeting({ ... });

if (!result.success) {
  if (isZodError(result.error)) {
    // TypeScript knows this is a ZodError
    result.error.errors.forEach(err => {
      console.log(err.path, err.message);
    });
  } else {
    // TypeScript knows this is a regular Error
    console.log(result.error.message);
  }
}

Custom Type Guards

import type { V2 } from "@meeting-baas/sdk";

function isBotCompleted(
  bot: V2.GetBotResponse
): bot is V2.GetBotResponse & { status: "completed" } {
  return bot.status === "completed";
}

const result = await v2Client.getBot({ bot_id: "123" });

if (result.success && isBotCompleted(result.data)) {
  // TypeScript knows status is "completed"
  console.log("Bot completed:", result.data.bot_id);
}

Webhook Types

v2 Webhook Types

All webhook types are available in the V2 namespace:

import type { V2 } from "@meeting-baas/sdk";

// Bot webhooks
type BotCompleted = V2.BotWebhookCompleted;
type BotFailed = V2.BotWebhookFailed;
type BotStatusChange = V2.BotWebhookStatusChange;

// Calendar webhooks
type ConnectionCreated = V2.CalendarWebhookConnectionCreated;
type EventCreated = V2.CalendarWebhookEventCreated;

// Callback types
type CallbackCompleted = V2.CallbackCompleted;
type CallbackFailed = V2.CallbackFailed;

Type-Safe Webhook Handler

import type { V2 } from "@meeting-baas/sdk";

type WebhookPayload =
  | V2.BotWebhookCompleted
  | V2.BotWebhookFailed
  | V2.BotWebhookStatusChange
  | V2.CalendarWebhookConnectionCreated
  | V2.CalendarWebhookEventCreated;

function handleWebhook(payload: WebhookPayload) {
  switch (payload.event) {
    case "bot.completed":
      // TypeScript infers the correct payload type
      console.log("Bot completed:", payload.data.bot_id);
      console.log("Recording:", payload.data.mp4_url);
      break;

    case "bot.failed":
      // TypeScript knows the payload structure
      console.error("Bot failed:", payload.data.error_message);
      break;

    case "bot.status_change":
      console.log("Status changed:", payload.data.status);
      break;

    case "calendar.connection.created":
      console.log("Calendar connected:", payload.data.calendar_id);
      break;

    case "calendar.event.created":
      console.log("Event created:", payload.data.event_id);
      break;

    default:
      // Exhaustive check
      const _exhaustive: never = payload;
      console.warn("Unknown event type");
  }
}

Generic Types

Working with Generic Response Types

async function fetchData\<T\>(
  operation: () => Promise<{ success: boolean; data?: T; error?: any }>
): Promise\<T\> {
  const result = await operation();

  if (result.success) {
    return result.data!;
  }

  throw new Error(result.error);
}

// Usage with type inference
const botId = await fetchData(() =>
  v2Client.createBot({
    meeting_url: "https://meet.google.com/abc",
    bot_name: "Bot"
  })
).then(data => data.bot_id);

Utility Types

Extract Type from Response

import type { V2 } from "@meeting-baas/sdk";

// Extract the data type from a response
type CreateBotData = Extract<
  Awaited<ReturnType<typeof v2Client.createBot>>,
  { success: true }
>["data"];

// Use the extracted type
const botData: CreateBotData = {
  bot_id: "123",
  status: "joining",
  meeting_url: "https://meet.google.com/abc",
  created_at: new Date().toISOString()
};

Conditional Types

type UnwrapResponse\<T\> = T extends { success: true; data: infer U }
  ? U
  : never;

// Extract data type from any response
type BotData = UnwrapResponse<
  Awaited<ReturnType<typeof v2Client.createBot>>
>;

Enum Types

Recording Modes

type RecordingMode =
  | "speaker_view"
  | "gallery_view"
  | "audio_only";

const mode: RecordingMode = "speaker_view";

await v1Client.joinMeeting({
  meeting_url: "...",
  bot_name: "Bot",
  reserved: true,
  recording_mode: mode
});

Calendar Platforms

type CalendarPlatform = "Google" | "Microsoft";

const platform: CalendarPlatform = "Google";

await client.createCalendar({
  oauth_client_id: "...",
  oauth_client_secret: "...",
  oauth_refresh_token: "...",
  platform: platform
});

Advanced Patterns

Builder Pattern with Types

import type { V2 } from "@meeting-baas/sdk";

class BotBuilder {
  private params: Partial<V2.CreateBotRequest> = {};

  meetingUrl(url: string) {
    this.params.meeting_url = url;
    return this;
  }

  name(name: string) {
    this.params.bot_name = name;
    return this;
  }

  image(url: string) {
    this.params.bot_image = url;
    return this;
  }

  webhook(url: string) {
    this.params.webhook_url = url;
    return this;
  }

  async create() {
    if (!this.params.meeting_url || !this.params.bot_name) {
      throw new Error("meeting_url and bot_name are required");
    }

    return await v2Client.createBot(
      this.params as V2.CreateBotRequest
    );
  }
}

// Usage
const result = await new BotBuilder()
  .meetingUrl("https://meet.google.com/abc")
  .name("My Bot")
  .image("https://example.com/bot.jpg")
  .webhook("https://example.com/webhook")
  .create();

Type-Safe Service Layer

import type { V2 } from "@meeting-baas/sdk";

interface BotService {
  createBot(params: V2.CreateBotRequest): Promise<V2.CreateBotResponse>;
  getBot(botId: string): Promise<V2.GetBotResponse>;
  stopBot(botId: string): Promise<void>;
}

class MeetingBaasBotService implements BotService {
  constructor(
    private client: ReturnType<typeof createBaasClient<"v2">>
  ) {}

  async createBot(params: V2.CreateBotRequest) {
    const result = await this.client.createBot(params);

    if (!result.success) {
      throw new Error(result.error);
    }

    return result.data;
  }

  async getBot(botId: string) {
    const result = await this.client.getBot({ bot_id: botId });

    if (!result.success) {
      throw new Error(result.error);
    }

    return result.data;
  }

  async stopBot(botId: string) {
    const result = await this.client.stopBot({ bot_id: botId });

    if (!result.success) {
      throw new Error(result.error);
    }
  }
}

Type Assertions

Safe Type Assertions

import type { V2 } from "@meeting-baas/sdk";

function isValidBot(data: unknown): data is V2.GetBotResponse {
  return (
    typeof data === "object" &&
    data !== null &&
    "bot_id" in data &&
    "status" in data
  );
}

// Usage
const data: unknown = await fetchBotData();

if (isValidBot(data)) {
  console.log("Bot ID:", data.bot_id);
  console.log("Status:", data.status);
}

Best Practices

  1. Use type inference: Let TypeScript infer types when possible

  2. Import types separately: Use import type for type-only imports

  3. Leverage discriminated unions: Use the success field for type narrowing

  4. Create type guards: Build reusable type guard functions

  5. Use const assertions: For literal type inference

const config = {
  api_key: "key",
  api_version: "v2" as const  // Literal type instead of string
};
  1. Avoid any: Use unknown and type guards instead

  2. Use strict mode: Enable strict: true in tsconfig.json

Complete Example

import { createBaasClient } from "@meeting-baas/sdk";
import type { V2 } from "@meeting-baas/sdk";

// Type-safe configuration
interface BotConfig {
  meetingUrl: string;
  botName: string;
  webhookUrl?: string;
}

// Type-safe service
class BotService {
  private client: ReturnType<typeof createBaasClient<"v2">>;

  constructor(apiKey: string) {
    this.client = createBaasClient({
      api_key: apiKey,
      api_version: "v2"
    });
  }

  async createBot(config: BotConfig): Promise<string> {
    const params: V2.CreateBotRequest = {
      meeting_url: config.meetingUrl,
      bot_name: config.botName,
      webhook_url: config.webhookUrl
    };

    const result = await this.client.createBot(params);

    if (!result.success) {
      throw new Error(`Failed to create bot: ${result.error}`);
    }

    return result.data.bot_id;
  }

  async waitForCompletion(botId: string): Promise<V2.GetBotResponse> {
    while (true) {
      const result = await this.client.getBot({ bot_id: botId });

      if (!result.success) {
        throw new Error(`Failed to get bot: ${result.error}`);
      }

      if (result.data.status === "completed" ||
          result.data.status === "failed") {
        return result.data;
      }

      await new Promise(resolve => setTimeout(resolve, 5000));
    }
  }
}

// Usage with full type safety
const service = new BotService(process.env.API_KEY!);

const botId = await service.createBot({
  meetingUrl: "https://meet.google.com/abc",
  botName: "Meeting Recorder"
});

const bot = await service.waitForCompletion(botId);
console.log("Bot completed:", bot.bot_id);

On this page