@coin-communities/sdk/react-query ships hook bindings around every operation in the SDK Reference, an optimistic-post reconciliation hook, plus a queryKeys factory for cache invalidation and prefetching. The entry also re-exports configureApi and the api namespace, so it's the only import path most React apps need.

@tanstack/react-query (v5+) is a peer dependency.


Setup

pnpm add @coin-communities/sdk @tanstack/react-query

Wrap your app in a QueryClientProvider and configure the SDK once at boot:

import { configureApi } from '@coin-communities/sdk/react-query';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
 
configureApi({
  baseUrl: 'https://api.coin-communities.xyz',
  auth: () => getToken(),
});
 
const queryClient = new QueryClient();
 
export function App({ children }: { children: React.ReactNode }) {
  return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>;
}

Conventions

  • Read operations (GET) are exposed as useXxx() hooks returning UseQueryResult<TData, TError>.
  • Write operations (POST / PUT / DELETE) are exposed as useXxx() hooks returning UseMutationResult<TData, TError, TVars>.
  • Hooks accept the standard React Query options object as their last argument (onSuccess, onError, select, enabled, …) — query/mutation keys and functions are managed for you.
  • Mutations that target a single resource (e.g. useRotateApiKey) take the resource id as the mutation variable rather than an options object.
  • Hooks scoped to a community (useMessages, usePostMessage, …) take tokenAddress as the first positional argument.

Hook catalog

Diagnostics

HookWrapsKind
useHealth(opts?)api.health()query
usePing(opts?)api.ping()query

Business — Auth

HookWrapsKind
useMe(opts?)api.me()query
useLogin(opts?)api.login()mutation (LoginRequest)
useLogout(opts?)api.logout()mutation (void)
useRegister(opts?)api.register()mutation (RegisterRequest)
useVerifyEmail(opts?)api.verifyEmail()mutation (VerifyEmailRequest)
useResendVerification(opts?)api.resendVerification()mutation (ResendVerificationRequest)
useForgotPassword(opts?)api.forgotPassword()mutation (ForgotPasswordRequest)
useResetPassword(opts?)api.resetPassword()mutation (ResetPasswordRequest)
useRefreshToken(opts?)api.refreshToken()mutation (RefreshTokenRequest)

Business — API Keys

HookWrapsKind
useApiKeys(opts?)api.getApiKeys()query
useCreateApiKey(opts?)api.createApiKey()mutation (CreateApiKeyRequest)
useRotateApiKey(opts?)api.rotateApiKey()mutation (id: string)
useRevokeApiKey(opts?)api.revokeApiKey()mutation (id: string)

Business — Server API Keys

HookWrapsKind
useServerApiKeys(opts?)api.getServerApiKeys()query
useCreateServerApiKey(opts?)api.createServerApiKey()mutation (CreateServerApiKeyRequest)
useRevokeServerApiKey(opts?)api.revokeServerApiKey()mutation (id: string)

Business — Callbacks

HookWrapsKind
useCallbacks(opts?)api.listCallbacks()query
useAddCallback(opts?)api.addCallback()mutation (AddCallbackRequest)
useRemoveCallback(opts?)api.removeCallback()mutation (id: string)

Business — Members

HookWrapsKind
useMembers(opts?)api.getMembers()query
useAddMember(opts?)api.addMember()mutation (AddMemberRequest)
useRemoveMember(opts?)api.deleteMember()mutation (id: string)

Business — Logs

HookWrapsKind
useLogs(params?, opts?)api.getLogs()query

Communities

HookWrapsKind
useCommunity(tokenAddress, opts?)api.getCommunity()query
useMessages(tokenAddress, query?, opts?)api.getMessages()query
useMessagesPublic(tokenAddress, opts?)api.getMessagesPublic()query
usePostMessage(tokenAddress, opts?)api.postMessage()mutation (PostMessageRequest)
useOptimisticCommunityPosts(tokenAddress, opts)realtime + message cache reconciliationlocal optimistic state
useLikeMessage(tokenAddress, opts?)api.likeMessage()mutation (messageId: string)
useUnlikeMessage(tokenAddress, opts?)api.unlikeMessage()mutation (messageId: string)
useLikeCount(tokenAddress, messageId, opts?)api.getLikeCount()query
useReportMessage(tokenAddress, opts?)api.reportMessage()mutation (ReportMessageRequest & { messageId })

Community media

HookWrapsKind
useUploadCommunityMedia(opts?)api.uploadCommunityMedia()mutation (UploadMediaRequest)
useMediaProviders(opts?)api.getMediaProviders()query

Chains & Feed

HookWrapsKind
useSupportedChains(opts?)api.getSupportedChains()query
useTopCommunities(query?, opts?)api.getTopCommunities()query
useFeed(opts?)api.getFeed()query
useFeedPublic(opts?)api.getFeedPublic()query

Users

HookWrapsKind
useUser(opts?)api.userMe()query

Wallets

HookWrapsKind
useWallets(opts?)api.getWallets()query
useWalletChallenge(opts?)api.walletChallenge()mutation (WalletChallengeRequest)
useLinkWallet(opts?)api.linkWallet()mutation (LinkWalletRequest)
useUnlinkWallet(opts?)api.unlinkWallet()mutation (id: string)

Twitter

HookWrapsKind
useTwitterAuthUrl(query, opts?)api.twitterAuthUrl()query
useTwitterCallback(opts?)api.twitterCallback()mutation (TwitterCallbackRequest)
useTwitterChallengeExchange(opts?)api.twitterChallengeExchange()mutation (TwitterChallengeExchangeRequest)

User Social Graph

HookWrapsKind
useUserProfile(userId, opts?)api.getUserProfile()query
useUserFollowers(userId, params?, opts?)api.getFollowers()query
useUserFollowing(userId, params?, opts?)api.getFollowing()query
useFollowUser(opts?)api.followUser()mutation (userId: string) — auto-invalidates profile + follow lists
useUnfollowUser(opts?)api.unfollowUser()mutation (userId: string) — auto-invalidates profile + follow lists

Message Replies

HookWrapsKind
useMessageReplies(tokenAddress, messageId, params?, opts?)api.getReplies()query
usePostReply(tokenAddress, messageId, opts?)api.postReply()mutation (PostMessageRequest) — auto-invalidates replies list

Community Members

HookWrapsKind
useCommunityMembers(tokenAddress, params?, opts?)api.getCommunityMembers()query

Realtime

useOptimisticCommunityPosts is the recommended companion to usePostMessage. It creates pending Message objects, subscribes to realtime events, reconciles clean or moderated message_update events, invalidates the message cache on resolution/gaps, and exposes mergePendingPosts(fetched) for rendering.

import { useOptimisticCommunityPosts } from '@coin-communities/sdk/react-query';
 
const optimistic = useOptimisticCommunityPosts(tokenAddress, {
  auth: {
    getTicket: async () => {
      const { data } = await api.getWsTicket({ path: { token_address: tokenAddress } });
      if (!data?.ticket) throw new Error('WebSocket ticket unavailable');
      return data.ticket;
    },
  },
  author: { userId: me.id, username: me.username },
  onReject: () => toast.error('Your post was removed by moderation.'),
  onModeration: () => toast.error('Your post was removed by moderation.'),
});
 
const feed = optimistic.mergePendingPosts(messages.data?.messages ?? []);

Use the lower-level realtime hooks from @coin-communities/sdk/react when you want to handle raw message_update, like_update, moderation_update, and onGap events yourself.

Business — API Key Origins

HookWrapsKind
useApiKeyOrigins(apiKeyId, opts?)api.listApiKeyOrigins()query
useAddApiKeyOrigin(opts?)api.addApiKeyOrigin()mutation (AddApiKeyOriginVariables) — auto-invalidates origins list
useRemoveApiKeyOrigin(opts?)api.removeApiKeyOrigin()mutation (RemoveApiKeyOriginVariables) — auto-invalidates origins list

queryKeys factory

queryKeys is a stable, typed object literal that exposes the cache key for every query the hooks use. Reach for it when you want to invalidate, prefetch, or read cache entries directly.

import { queryKeys } from '@coin-communities/sdk/react-query';
 
queryKeys.health;                                                    // ['health']
queryKeys.ping;                                                      // ['ping']
queryKeys.auth.me;                                                   // ['auth', 'me']
queryKeys.apiKeys.all;                                               // ['apiKeys']
queryKeys.apiKeys.list();                                            // ['apiKeys', 'list']
queryKeys.serverApiKeys.all;                                         // ['serverApiKeys']
queryKeys.serverApiKeys.list();                                      // ['serverApiKeys', 'list']
queryKeys.apiKeyOrigins.all;                                         // ['apiKeyOrigins']
queryKeys.apiKeyOrigins.list(apiKeyId);                              // ['apiKeyOrigins', apiKeyId, 'list']
queryKeys.callbacks.all;                                             // ['callbacks']
queryKeys.members.all;                                               // ['members']
queryKeys.logs.list({ startTime: '2024-01-01T00:00:00Z' });          // ['logs', 'list', params]
queryKeys.feed;                                                      // ['feed']
queryKeys.feedPublic;                                                // ['feed', 'public']
queryKeys.user.me;                                                   // ['user', 'me']
queryKeys.user.wallets;                                              // ['user', 'wallets']
queryKeys.user.profile(userId);                                      // ['user', userId, 'profile']
queryKeys.user.followers(userId, params?);                           // ['user', userId, 'followers', params]
queryKeys.user.following(userId, params?);                           // ['user', userId, 'following', params]
queryKeys.community.all;                                             // ['community']
queryKeys.community.top(query?);                                     // ['community', 'top', query]
queryKeys.community.detail(tokenAddress);                            // ['community', tokenAddress]
queryKeys.community.messages(tokenAddress, q);                       // ['community', tokenAddress, 'messages', q]
queryKeys.community.messagesPublic(tokenAddress);                    // ['community', tokenAddress, 'messages', 'public']
queryKeys.community.likeCount(tokenAddress, mid);                    // ['community', tokenAddress, 'messages', mid, 'likes']
queryKeys.community.replies(tokenAddress, messageId, params?);       // ['community', tokenAddress, 'messages', messageId, 'replies', params]
queryKeys.community.members(tokenAddress, params?);                  // ['community', tokenAddress, 'members', params]
queryKeys.media.providers;                                           // ['media', 'providers']
queryKeys.chains.supported;                                          // ['chains', 'supported']
queryKeys.twitter.authUrl(query);                                    // ['twitter', 'auth-url', query]

The *.all keys are deliberately broad — pass them to queryClient.invalidateQueries({ queryKey }) to refresh every variant of a resource at once.


Mutation variable types

AddApiKeyOriginVariables

Variables accepted by useAddApiKeyOrigin.

interface AddApiKeyOriginVariables {
  apiKeyId: string;
  origin: string;
}
  • apiKeyId (string) — the API key to add the origin to.
  • origin (string) — the origin to allow (e.g. 'https://app.example.com').

RemoveApiKeyOriginVariables

Variables accepted by useRemoveApiKeyOrigin.

interface RemoveApiKeyOriginVariables {
  apiKeyId: string;
  originId: string;
}
  • apiKeyId (string) — the API key that owns the origin entry.
  • originId (string) — the id of the ApiKeyOrigin entry to remove.

End-to-end example

A list-and-create flow that invalidates the list cache after a successful mutation:

import {
  queryKeys,
  useApiKeys,
  useCreateApiKey,
} from '@coin-communities/sdk/react-query';
import { useQueryClient } from '@tanstack/react-query';
 
export function ApiKeysList() {
  const qc = useQueryClient();
  const keys = useApiKeys();
  const create = useCreateApiKey({
    onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.apiKeys.all }),
  });
 
  if (keys.isLoading) return <p>Loading…</p>;
  if (keys.isError) return <p>Failed to load API keys.</p>;
 
  return (
    <>
      <ul>{keys.data?.map((k) => <li key={k.id}>{k.name}</li>)}</ul>
      <button
        type="button"
        disabled={create.isPending}
        onClick={() => create.mutate({ name: 'CI key' })}
      >
        Create key
      </button>
    </>
  );
}