Skip to main content

Overview

The Quests API provides endpoints for retrieving quest information, tracking quest progress, and managing quest completion. All endpoints require authentication.

Queries

get

Retrieves a single quest by ID.
import { api } from "@/convex/_generated/api";
import { useQuery } from "convex/react";

const quest = useQuery(api.quests.get, { questId });
questId
Id<'quests'>
required
The unique identifier of the quest to retrieve
quest
Quest
The quest object with all fields
Quest Object Structure:
_id
Id<'quests'>
Unique quest identifier
name
string
Quest name
description
string
Detailed quest description
estimatedTime
number
Estimated time to complete in minutes
difficulty
'einfach' | 'mittel' | 'schwer'
Quest difficulty level (easy, medium, hard in German)
xp
number
Experience points awarded for completion
category
'abenteuer' | 'geschichte' | 'kultur' | 'natur' | 'essen' | 'trinken'
Quest category (adventure, history, culture, nature, food, drinks in German)
imageUrl
string
URL to the quest’s cover image
Errors:
  • “Quest not found” - The specified quest ID doesn’t exist
  • “Not authenticated” - User is not logged in

listRecommended

Returns all quests that the user hasn’t completed yet.
import { api } from "@/convex/_generated/api";
import { useQuery } from "convex/react";

const recommendedQuests = useQuery(api.quests.listRecommended);
quests
Quest[]
Array of quests the user hasn’t completed
Example Response:
[
  {
    _id: "quest123",
    name: "Historical Berlin",
    description: "Explore the rich history of Berlin",
    estimatedTime: 120,
    difficulty: "mittel",
    xp: 100,
    category: "geschichte",
    imageUrl: "https://..."
  },
  // ... more quests
]
Errors:
  • “Not authenticated” - User is not logged in

listNew

Returns quests created within the last 7 days that the user hasn’t completed.
import { api } from "@/convex/_generated/api";
import { useQuery } from "convex/react";

const newQuests = useQuery(api.quests.listNew);
quests
Quest[]
Array of new quests (created in the last 7 days) that the user hasn’t completed
Implementation:
const SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1000;
const cutoff = Date.now() - SEVEN_DAYS_MS;

return allQuests.filter(
  (quest) =>
    quest._creationTime >= cutoff && !completedQuestIds.has(quest._id),
);
Errors:
  • “Not authenticated” - User is not logged in

listFinished

Returns all quests the user has completed.
import { api } from "@/convex/_generated/api";
import { useQuery } from "convex/react";

const finishedQuests = useQuery(api.quests.listFinished);
quests
Quest[]
Array of quests the user has completed
Errors:
  • “Not authenticated” - User is not logged in

listInProgress

Returns quest IDs for all quests the user has started but not completed.
import { api } from "@/convex/_generated/api";
import { useQuery } from "convex/react";

const inProgressQuestIds = useQuery(api.quests.listInProgress);
questIds
Id<'quests'>[]
Array of quest IDs currently in progress
Example Response:
["quest123", "quest456", "quest789"]
Implementation:
const userQuests = await ctx.db
  .query("userQuests")
  .withIndex("by_user", (q) => q.eq("userId", user._id))
  .filter((q) => q.eq(q.field("completedAt"), undefined))
  .collect();

return userQuests.map((uq) => uq.questId);
Errors:
  • “Not authenticated” - User is not logged in

getStatus

Retrieves the user’s progress status for a specific quest.
import { api } from "@/convex/_generated/api";
import { useQuery } from "convex/react";

const questStatus = useQuery(api.quests.getStatus, { questId });
questId
Id<'quests'>
required
The quest ID to check status for
userQuest
UserQuest | null
The user’s quest progress, or null if not started
UserQuest Object:
_id
Id<'userQuests'>
Unique user quest record identifier
userId
Id<'users'>
The user’s ID
questId
Id<'quests'>
The quest ID
startedAt
number
Unix timestamp when the quest was started
completedAt
number | undefined
Unix timestamp when the quest was completed (undefined if in progress)
Example Response:
{
  _id: "userQuest123",
  userId: "user456",
  questId: "quest789",
  startedAt: 1709481600000,
  completedAt: undefined // or timestamp if completed
}
Errors:
  • “Not authenticated” - User is not logged in

Mutations

start

Starts a quest for the current user.
import { api } from "@/convex/_generated/api";
import { useMutation } from "convex/react";

const startQuest = useMutation(api.quests.start);

await startQuest({ questId });
questId
Id<'quests'>
required
The ID of the quest to start
userQuestId
Id<'userQuests'>
The ID of the newly created user quest record
Implementation:
return await ctx.db.insert("userQuests", {
  userId: user._id,
  questId,
  startedAt: Date.now(),
});
Errors:
  • “Quest not found” - The specified quest ID doesn’t exist
  • “Quest already in progress” - User has already started this quest
  • “Quest already completed” - User has already completed this quest
  • “Not authenticated” - User is not logged in

complete

Marks a quest as completed for the current user. Validates that all locations have been visited.
import { api } from "@/convex/_generated/api";
import { useMutation } from "convex/react";

const completeQuest = useMutation(api.quests.complete);

await completeQuest({ questId });
questId
Id<'quests'>
required
The ID of the quest to complete
Validation Logic: The mutation performs comprehensive validation:
  1. Checks if the quest has been started
  2. Checks if the quest is already completed
  3. Retrieves all locations for the quest
  4. Retrieves all user-completed locations
  5. Verifies that every required location has been visited
const requiredIds = new Set(questLocations.map((l) => l._id));
const completedIds = new Set(completedLocations.map((cl) => cl.locationId));
if ([...requiredIds].some((id) => !completedIds.has(id)))
  throw new ConvexError("Quest not finished");
Errors:
  • “Quest not started” - User hasn’t started this quest yet
  • “Quest already completed” - User has already completed this quest
  • “Quest not finished” - Not all required locations have been visited
  • “Not authenticated” - User is not logged in
Example Usage:
import { api } from "@/convex/_generated/api";
import { useMutation, useQuery } from "convex/react";

function QuestCompletion({ questId }) {
  const completeQuest = useMutation(api.quests.complete);
  const locations = useQuery(api.locations.listByQuest, { questId });
  const completed = useQuery(api.locations.listCompleted, { questId });

  const allLocationsVisited = 
    locations?.length === completed?.length;

  const handleComplete = async () => {
    try {
      await completeQuest({ questId });
      // Show success message
    } catch (error) {
      // Handle errors: "Quest not finished", etc.
    }
  };

  return (
    <button 
      onClick={handleComplete}
      disabled={!allLocationsVisited}
    >
      Complete Quest
    </button>
  );
}

Build docs developers (and LLMs) love