Skip to main content

V1 to V2 Migration

This guide covers migrating from Fanfare SDK v1.x to v2.x. Version 2.0 introduces significant improvements including a new journey-based API, better state management, and SolidJS widgets.

Breaking Changes Summary

CategoryV1V2
Initializationnew FanfareClient()Fanfare.init()
StateCallbacksNanostores
JourneyManual state managementExperienceJourney state machine
WidgetsNoneSolidJS web components
AuthenticationSeparate auth flowIntegrated with SDK
EventsEvent emitterTyped event system

Installation

# Remove v1
npm uninstall @waitify-io/fanfare-client

# Install v2
npm install @waitify-io/fanfare-sdk-core @waitify-io/fanfare-sdk-react

Initialization

V1

import { FanfareClient } from "@waitify-io/fanfare-client";

const client = new FanfareClient({
  apiKey: "pk_live_xxx",
  organizationId: "org_xxx",
});

await client.connect();

V2

import { Fanfare } from "@waitify-io/fanfare-sdk-core";

const sdk = await Fanfare.init({
  organizationId: "org_xxx",
  publishableKey: "pk_live_xxx",
  // Optional: Restore existing session
  // sessionToken: existingToken,
});

Authentication

V1

// V1: Separate authentication
const session = await client.auth.login(email);
await client.auth.verifyOtp(otp);

V2

// V2: Integrated auth module
const { challenge } = await sdk.auth.otp.start({ email });
const session = await sdk.auth.otp.verify({ code: otp });

// Or guest authentication
const session = await sdk.auth.guest();

Queue Operations

V1

// V1: Direct queue methods
const queue = await client.queues.get("queue_123");
await client.queues.enter("queue_123");

client.on("queue:position", (position) => {
  console.log("Position:", position);
});

client.on("queue:admitted", (data) => {
  window.location.href = `/checkout?token=${data.token}`;
});

V2

// V2: Module-based with reactive state
const queueModule = sdk.queues;

// Enter queue
const state = await queueModule.enter("queue_123");

// Subscribe to state changes
queueModule.subscribe("queue_123", (state) => {
  if (state.status === "QUEUED") {
    console.log("Position:", state.position);
  }
  if (state.status === "ADMITTED") {
    window.location.href = `/checkout?token=${state.admittanceToken}`;
  }
});

React Hooks

V1

// V1: Custom hooks with callbacks
import { useQueue } from "@waitify-io/fanfare-client-react";

function QueueComponent() {
  const { position, enter, leave } = useQueue("queue_123", {
    onAdmitted: (token) => {
      window.location.href = `/checkout?token=${token}`;
    },
  });

  return <div>Position: {position}</div>;
}

V2

// V2: Reactive hooks with status
import { useQueue, useFanfareAuth } from "@waitify-io/fanfare-sdk-react";

function QueueComponent() {
  const { isAuthenticated, guest } = useFanfareAuth();
  const { status, position, admittanceToken, enter, leave, isLoading } = useQueue("queue_123");

  const handleEnter = async () => {
    if (!isAuthenticated) await guest();
    await enter();
  };

  if (status === "admitted" && admittanceToken) {
    window.location.href = `/checkout?token=${admittanceToken}`;
    return null;
  }

  return (
    <div>
      {status === "queued" && <p>Position: {position}</p>}
      {status === "not_entered" && <button onClick={handleEnter}>Enter Queue</button>}
    </div>
  );
}

Draw Operations

V1

// V1
await client.draws.enter("draw_123");

client.on("draw:result", (result) => {
  if (result.won) {
    console.log("You won!");
  }
});

V2

// V2
const state = await sdk.draws.enter("draw_123");

sdk.draws.subscribe("draw_123", (state) => {
  if (state.status === "WON") {
    console.log("You won!");
  }
});

Experience Journey (New in V2)

V2 introduces ExperienceJourney for managing complex multi-step flows:
import { ExperienceJourney } from "@waitify-io/fanfare-sdk-core";

const journey = new ExperienceJourney(sdk);

// Start journey
await journey.start("exp_123");

// Listen to state changes
journey.state.listen((snapshot) => {
  switch (snapshot.journeyStage) {
    case "needs_auth":
      // Show auth form
      break;
    case "needs_access_code":
      // Show access code input
      break;
    case "routed":
      // Handle based on sequence stage
      break;
  }
});

// Perform actions
await journey.perform("enter_queue");
await journey.perform("leave_participation");

Event System

V1

// V1: String-based events
client.on("queue:position", handler);
client.off("queue:position", handler);

V2

// V2: Typed event system
import type { SDKEvents } from "@waitify-io/fanfare-sdk-core";

sdk.events.on("queue:position-updated", (event) => {
  // event is fully typed
  console.log(event.queueId, event.position);
});

sdk.events.off("queue:position-updated", handler);

State Management

V1

// V1: Callback-based updates
client.on("queue:state", (state) => {
  updateUI(state);
});

V2

// V2: Nanostores for reactive state
import { useStore } from "@nanostores/react";

// In React
function QueueStatus() {
  const state = useStore(sdk.queues.getStore("queue_123"));
  return <div>Status: {state?.status}</div>;
}

// Or subscribe directly
const unsubscribe = sdk.queues.getStore("queue_123").subscribe((state) => {
  updateUI(state);
});

Provider Setup

V1

// V1
import { FanfareProvider } from "@waitify-io/fanfare-client-react";

function App() {
  return (
    <FanfareProvider apiKey="pk_live_xxx" organizationId="org_xxx">
      <YourApp />
    </FanfareProvider>
  );
}

V2

// V2
import { FanfareProvider } from "@waitify-io/fanfare-sdk-react";

function App() {
  return (
    <FanfareProvider organizationId="org_xxx" publishableKey="pk_live_xxx">
      <YourApp />
    </FanfareProvider>
  );
}

Web Components (New in V2)

V2 adds web component support:
import { registerWebComponents } from "@waitify-io/fanfare-sdk-solid";

registerWebComponents({
  organizationId: "org_xxx",
  publishableKey: "pk_live_xxx",
});

// Use in any framework
function QueuePage() {
  return <fanfare-queue-widget queue-id="queue_123" />;
}

Type Changes

Session Types

// V1
interface Session {
  token: string;
  userId: string;
}

// V2
interface Session {
  sessionToken: string;
  consumerId: string;
  organizationId: string;
  expiresAt?: number;
  isGuest: boolean;
}

Queue State Types

// V1
interface QueueState {
  position: number;
  admitted: boolean;
  token?: string;
}

// V2
type QueueConsumerState =
  | { status: "NOT_ENTERED" }
  | { status: "QUEUED"; position: number; estimatedWaitTimeInSeconds?: number }
  | { status: "ADMITTED"; admittanceToken: string; expiresAt: number };

Migration Checklist

  • Update package dependencies
  • Replace FanfareClient with Fanfare.init()
  • Update authentication flow
  • Replace event callbacks with subscriptions
  • Update React hooks usage
  • Fix TypeScript type errors
  • Test all distribution flows
  • Update error handling
  • Consider using web components