Skip to main content
Demo
Viewing as
Fan identity

The SDK

Build your fan app on our platform.

The SDK revolution.fan itself runs on. A creator's page, whether a band, a club, a streamer or a venue, with its live wall, follows, comments, hearts and polls, Scout, and the fan's account and everything they follow: one typed client, and React hooks for the web and React Native. Your brand on top, the platform underneath.

TypeScriptReactReact NativeContract-tested

Partner preview

Partners get the packages with their access; they are not on npm yet. Version 1 covers the 25 operations below. Sign-in by phone, push notifications, messages and Scout's voice come next. To build on it, tell us what you're building.

How it fits together

The contractEvery operation the platform promises: its address, who may call it, what it takes and the exact shape of its answer. Checked against the live API, signed in and signed out, on every change to the platform.
@revolution/clientOne typed function per operation, generated from the contract. No dependencies: the browser's, React Native's or Node's own fetch.
@revolution/reactA hook for every operation, on your app's own TanStack Query cache, for the web and React Native alike.

Quick start

The client

typescript
import { createClient } from "@revolution/client";

const api = createClient({
  baseUrl: "https://api.revolution.fan",
  // The signed-in fan's ID token, or null when nobody is signed in.
  getToken: () => auth.currentUser?.getIdToken() ?? null,
  appKey: "your-app-key",
});

const hub = await api.getHub({ path: { kind: "artist", slug: "an-artist" } });
hub.universe.name;        // the creator
hub.now.state;            // "quiet" | "live" | "show_tonight" | "release"
hub.shelves.items.length; // what their sources pulled onto the wall

await api.postComment({
  path: { kind: "item", sid: hub.shelves.items[0].id },
  body: { body: "See you there" },
});

The hooks

tsx
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import {
  RevolutionProvider, useHub, useFollowState, useToggleFollow,
} from "@revolution/react";

const queryClient = new QueryClient();

export function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <RevolutionProvider client={api}>
        <CreatorPage kind="artist" slug="an-artist" />
      </RevolutionProvider>
    </QueryClientProvider>
  );
}

function CreatorPage({ kind, slug }: { kind: string; slug: string }) {
  const { data: hub } = useHub(kind, slug);
  const { data: follow } = useFollowState(kind, slug);
  const toggle = useToggleFollow(kind, slug);
  if (!hub) return null;
  return (
    <button onClick={() => toggle.mutate(!follow?.following)}>
      {follow?.following ? "Following" : "Follow"} {hub.universe.name}
    </button>
  );
}

The cache is your app's own: its QueryClient, its defaults, its devtools. The provider only hands the hooks a client. Each hook has an options factory beside it (hubQuery, toggleFollowMutation and the rest), so a server can prefetch a page before it renders.

What version 1 covers

A creator's page

getHub, getPeople, getFollow, follow, unfollow, getCollection

Anyone reads the page; following and the fan's own collection need a signed-in fan

The owner's desk

getDeskItems, setItemState

The page's owner, to hide what their sources pulled in or put it back

A fan's follows

getLineup, getFollowsBinge

Signed-in fans

Talk on anything

getComments, postComment, deleteComment, getCommentCounts, toggleHeart, getHeartCounts, getPoll, answerPoll

Anyone reads; posting, hearts and answers need a signed-in fan

Scout

askScout, getScoutThread, markScoutThreadRead, getScoutInbox, getScoutObsessions

Anyone can ask; the thread, inbox and watches belong to a signed-in fan

The account

getMe, getProfile

The fan's own record when signed in; anyone's public profile

Comments, hearts and polls work on anything with an id: a post on a creator's wall, a show, a stream. The kind is part of the address.

Hooks

Every operation has its hook. What the ones that write do to the cache:

useHubThe whole page in one read, fresh for as long as the page itself says, and read again on that beat while it is on screen.
useFollowState, useToggleFollowThe button turns on the tap and turns back if the server refuses; the fan's follows and the follower count update after.
useToggleHeart, useHeartCountsA heart updates the count and the fan's own heart everywhere that item shows.
usePostComment, useDeleteCommentThe thread updates in place, replies under their comment.
useSetItemStateA hidden item leaves every cached page and feed at once.
useAskScoutOne turn with Scout; a signed-in fan's saved thread reads again after it.

Signing in

The SDK carries the fan's own session: getToken returns the signed-in fan's ID token from the platform's sign-in, which partners set up with their access, or null. Reads that anyone can make answer signed out (a follow state is simply false); everything that belongs to a fan answers 401 until one signs in.

Errors and timeouts

typescript
import { ApiError } from "@revolution/client";

try {
  await api.getHub({ path: { kind: "artist", slug: "nobody" } });
} catch (err) {
  if (err instanceof ApiError) {
    err.operation; // "getHub"
    err.status;    // 404
    err.code;      // the server's error code, when it sends one
  }
}

Any answer other than an operation's own success status throws an ApiError. A request is abandoned after 15 seconds, a turn with Scout after 45.

What we promise

Every operation in version 1 is called against a real database on every change to the platform, signed in and signed out, and its answer is checked against the schema the types are generated from. A field the SDK lists is never removed or retyped without a new version; new fields can appear at any time, and the types show only what is promised. The client has no dependencies; the hooks need React 18 or 19 and TanStack Query 5.

Other ways in