Skip to main content

Queries

viewer

Returns the currently authenticated user’s profile.
import { useQuery } from "convex/react";
import { api } from "@/convex/_generated/api";

const user = useQuery(api.users.viewer);
Returns: User | null

getRank

Returns the user’s global leaderboard rank based on XP.
const rank = useQuery(api.users.getRank);
// Returns: 42 (user is ranked #42)
Returns: number Rank is calculated by counting users with higher XP. Anonymous users are excluded from rankings.
export const getRank = query({
  args: {},
  handler: async (ctx) => {
    const userId = await getAuthUserId(ctx);
    if (!userId) return 0;

    const user = await ctx.db.get(userId);
    if (!user || user.xp === undefined) return 0;

    const betterUsers = await ctx.db
      .query("users")
      .withIndex("by_xp", q => q.gt("xp", user.xp!))
      .filter(q => q.neq(q.field("isAnonymous"), true))
      .collect();
    
    return betterUsers.length + 1;
  }
});

getTopUsers

Returns the top players on the global leaderboard.
limit
number
default:50
Maximum number of users to return (default: 50)
const topPlayers = useQuery(api.users.getTopUsers, { limit: 10 });
Returns: Array<TopUser>

searchUsers

Search for users by name or email (for sending friend requests).
query
string
required
Search query (minimum 2 characters)
const results = useQuery(api.users.searchUsers, { query: "john" });
Returns: Array<SearchResult> (max 5 results)
Search excludes the current user and anonymous users. Returns up to 5 matches.

getRecentBadges

Returns the user’s 3 most recently unlocked badges.
const badges = useQuery(api.users.getRecentBadges);
Returns: Array<Badge>

Available Badges

const BADGES_META = {
  'hello-world': { title: '"Hello World" Master', icon: 'BookOpen' },
  'streak-5': { title: '5 Day Streak', icon: 'Flame' },
  'bug-hunter': { title: 'Bug Hunter', icon: 'Bug' },
  'algo-architect': { title: 'Algo Architect', icon: 'Cpu' },
  'css-wizard': { title: 'CSS Wizard', icon: 'Palette' },
};

Mutations

updateName

Update the user’s display name.
name
string
required
New display name
const updateName = useMutation(api.users.updateName);
await updateName({ name: "CodeMaster" });

updateImage

Update the user’s custom avatar.
image
string
required
Avatar URL or base64-encoded image
const updateImage = useMutation(api.users.updateImage);
await updateImage({ image: "https://example.com/avatar.png" });

updatePreferredLanguage

Set the user’s preferred programming language.
language
string
required
Language identifier (e.g., “javascript”, “python”, “cpp”)
const updateLang = useMutation(api.users.updatePreferredLanguage);
await updateLang({ language: "typescript" });

heartbeat

Update the user’s last seen timestamp to track presence.
const heartbeat = useMutation(api.users.heartbeat);

// Call periodically (e.g., every 30 seconds)
setInterval(() => heartbeat(), 30000);
Call this mutation periodically to keep the user’s online status up-to-date.

awardBadge

Award a badge to the current user (typically called after completing achievements).
badgeId
string
required
Badge identifier to award
const awardBadge = useMutation(api.users.awardBadge);
await awardBadge({ badgeId: "hello-world" });
If the user already has the badge, this operation is idempotent (no duplicate badges).

Example: Complete Profile Component

import { useQuery, useMutation } from "convex/react";
import { api } from "@/convex/_generated/api";

function UserProfile() {
  const user = useQuery(api.users.viewer);
  const rank = useQuery(api.users.getRank);
  const badges = useQuery(api.users.getRecentBadges);
  const updateName = useMutation(api.users.updateName);
  
  if (!user) return <div>Please sign in</div>;
  
  return (
    <div>
      <img src={user.customAvatar || user.image} alt={user.name} />
      <h1>{user.name}</h1>
      <p>Level {user.level} • {user.xp} XP</p>
      <p>Global Rank: #{rank}</p>
      <p>Streak: {user.streak} days 🔥</p>
      
      <div>
        <h2>Recent Badges</h2>
        {badges?.map(badge => (
          <div key={badge._id}>
            {badge.meta.title} {badge.meta.icon}
          </div>
        ))}
      </div>
      
      <button onClick={() => updateName({ name: "New Name" })}>
        Update Name
      </button>
    </div>
  );
}

Build docs developers (and LLMs) love