Skip to Content
Next.jsSetup

Setup

Most hooks require a WatermelonDB database, an auth token, and optionally wallet credentials for encrypted file storage. This page shows how to obtain each value.

Database

database is a WatermelonDB instance created with useDatabaseManager from the SDK. It’s scoped to the user’s wallet address so each user gets their own local store. Expose it via React context so hooks can access it with useDatabase().

// Create a WatermelonDB instance scoped to the user's wallet address. // Expose via React context so hooks can access it with useDatabase(). export function setupDatabase() { const { user } = usePrivy(); const database = useDatabaseManager(user?.wallet?.address, dbManager); return database; }

Authentication

getToken returns a Privy identity token. It caches the token from useIdentityToken() and refreshes it when the JWT expires, avoiding redundant API calls to /api/v1/users/me.

// Cache the Privy identity token and refresh when the JWT expires. // This avoids calling Privy's standalone getIdentityToken() on every // request (which hits /api/v1/users/me each time). export function setupGetToken() { const { identityToken } = useIdentityToken(); const identityTokenRef = useRef(identityToken); const tokenWaitersRef = useRef<Array<(token: string | null) => void>>([]); useEffect(() => { identityTokenRef.current = identityToken; if (identityToken && tokenWaitersRef.current.length > 0) { for (const resolve of tokenWaitersRef.current) resolve(identityToken); tokenWaitersRef.current = []; } }, [identityToken]); const getToken = useCallback(async (): Promise<string | null> => { const cached = identityTokenRef.current; if (cached) { try { const payload = JSON.parse(atob(cached.split(".")[1])); if (payload.exp && payload.exp * 1000 > Date.now() + 30_000) { return cached; } } catch { // Fall through to refresh } try { const fresh = await fetchIdentityToken(); if (fresh) { identityTokenRef.current = fresh; return fresh; } } catch { // Network error — fall through to waiter } } return new Promise((resolve) => { tokenWaitersRef.current.push(resolve); setTimeout(() => { tokenWaitersRef.current = tokenWaitersRef.current.filter( (r) => r !== resolve ); resolve(identityTokenRef.current); }, 10_000); }); }, []); return getToken; }

Wallet and Signing

walletAddress and the signing functions come from Privy’s auth hooks. signMessage prompts the user to sign (used to derive an encryption key for file storage in OPFS), while embeddedWalletSigner signs silently via Privy’s embedded wallet.

// Get wallet address and signing functions from Privy. // signMessage prompts the user; embeddedWalletSigner signs silently. export function setupWallet() { const { user, signMessage: privySignMessage } = usePrivy(); const { wallets } = useWallets(); const walletAddress = user?.wallet?.address; const signMessage = useCallback( async (message: string) => { const result = await privySignMessage( { message }, { uiOptions: { showWalletUIs: false } } ); return result.signature; }, [privySignMessage] ); const embeddedWallet = wallets.find((w) => w.walletClientType === "privy"); const embeddedWalletSigner = useCallback( async (message: string) => { if (!embeddedWallet?.address) throw new Error("Embedded wallet not ready"); const result = await privySignMessage( { message }, { uiOptions: { showWalletUIs: false } } ); return result.signature; }, [embeddedWallet, privySignMessage] ); return { walletAddress, signMessage, embeddedWalletSigner }; }
Last updated on