Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions convex/subscriptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ const createCheckout = async ({
customerEmail: customerEmail,
metadata: {
...metadata,
priceId: productPriceId,
},
};

Expand Down Expand Up @@ -185,7 +184,7 @@ export const checkUserSubscriptionStatus = query({
}
tokenIdentifier = identity.subject;
}

const user = await ctx.db
.query("users")
.withIndex("by_token", (q) => q.eq("tokenIdentifier", tokenIdentifier))
Expand All @@ -194,10 +193,11 @@ export const checkUserSubscriptionStatus = query({
if (!user) {
return { hasActiveSubscription: false };
}

const subscription = await ctx.db
.query("subscriptions")
.withIndex("userId", (q) => q.eq("userId", user.tokenIdentifier))
.order("desc") // Order by createdAt in descending order
.first();
Comment on lines +200 to 201
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

“Order by createdAt” isn’t achieved; current index doesn’t guarantee latest subscription

.withIndex("userId", …).order("desc") will order by the index key (userId) and then by internal doc id, not by createdAt. This can return an arbitrary subscription for the user and lead to incorrect hasActiveSubscription results.

Prefer ordering by a time field that exists on the subscription document and is indexed as a secondary key. Since you already persist startedAt, index on (userId, startedAt) and order desc to get the most recent subscription for the user.

Apply this change in the query to use a compound index:

-      .withIndex("userId", (q) => q.eq("userId", user.tokenIdentifier))
-      .order("desc")  // Order by createdAt in descending order
+      .withIndex("by_userId_startedAt", (q) => q.eq("userId", user.tokenIdentifier))
+      .order("desc")  // Latest by startedAt

Add the corresponding index in your Convex schema (example):

// convex/schema.ts
defineTable({
  // ... other fields ...
  userId: v.string(),
  startedAt: v.number(), // already present in your inserts
})
  .index("by_userId", ["userId"])
  .index("by_userId_startedAt", ["userId", "startedAt"]);

Verification script to confirm the index exists in your schema and to find all places to update:


🏁 Script executed:

#!/bin/bash
# Look for Convex schema and check for the compound index and subscriptions table fields.
fd -a -t f 'schema' | while read -r f; do
  echo "== $f ==";
  rg -n -C2 -H 'defineTable|subscriptions|index\(|startedAt' "$f";
done

# Find all queries over "subscriptions" using the "userId" index
rg -n -C2 --type=ts '\.query\("subscriptions"\).*?\.withIndex\("userId"' convex

Length of output: 2415


#!/bin/bash
# Confirm the subscription query uses withIndex("userId") and ordering on createdAt
rg -n 'withIndex\("userId"' -C3 --type=ts convex/subscriptions.ts
rg -n 'query\("subscriptions"' -C3 --type=ts convex/subscriptions.ts

#!/bin/bash
# Display the query on subscriptions and its index usage around withIndex("userId")
rg -nP 'query\("subscriptions".*?withIndex\("userId"' -C4 convex/subscriptions.ts

Update subscription query to use a compound index on startedAt and add that index

The current query in convex/subscriptions.ts is still using:

.withIndex("userId", (q) => q.eq("userId", user.tokenIdentifier))
.order("desc")  // Order by createdAt in descending order
.first();

That will sort by the index key (userId) and then by internal document ID—not by your timestamp field—so you won’t reliably get the most recent subscription.

Please make the following critical fixes:

• In convex/subscriptions.ts (around lines 200–201), change to:

-      .withIndex("userId", (q) => q.eq("userId", user.tokenIdentifier))
-      .order("desc")  // Order by createdAt in descending order
+      .withIndex("by_userId_startedAt", (q) =>
+        q.eq("userId", user.tokenIdentifier)
+      )
+      .order("desc")  // Latest by startedAt
       .first();

• In your schema file (convex/schema.ts), add the compound index on startedAt:

 subscriptions: defineTable({
   userId: v.optional(v.string()),
   // … other fields …
   startedAt: v.optional(v.number()),
 })
-  .index("userId", ["userId"])
+  .index("userId", ["userId"])
+  .index("by_userId_startedAt", ["userId", "startedAt"])
   .index("polarId", ["polarId"]),

After deploying these changes, your .order("desc") will correctly return the subscription with the highest startedAt for a given user.

🤖 Prompt for AI Agents
In convex/subscriptions.ts around lines 200–201, the query uses
.withIndex("userId", ...) which sorts by userId and internal ID instead of your
timestamp; update the query to use a compound index that includes startedAt
(e.g., .withIndex("userId_startedAt", q => q.eq("userId",
user.tokenIdentifier)).order("desc").first()) so the .order("desc") sorts by
startedAt; then in convex/schema.ts add a compound index declaration for the
subscriptions table that includes userId and startedAt (e.g., an index named
userId_startedAt with keys [userId, startedAt]) and deploy the schema so the
query can use that new index.


const hasActiveSubscription = subscription?.status === "active";
Expand Down Expand Up @@ -234,6 +234,7 @@ export const checkUserSubscriptionStatusByClerkId = query({
const subscription = await ctx.db
.query("subscriptions")
.withIndex("userId", (q) => q.eq("userId", user.tokenIdentifier))
.order("desc") // Order by createdAt in descending order
.first();
Comment on lines +237 to 238
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Same ordering issue here — switch to a compound (userId, startedAt) index

This query also won’t reliably return the latest subscription for the user. Use the same index and ordering approach as above.

Apply this change:

-      .withIndex("userId", (q) => q.eq("userId", user.tokenIdentifier))
-      .order("desc")  // Order by createdAt in descending order
+      .withIndex("by_userId_startedAt", (q) => q.eq("userId", user.tokenIdentifier))
+      .order("desc")  // Latest by startedAt
🤖 Prompt for AI Agents
In convex/subscriptions.ts around lines 237-238, the query currently orders by
createdAt desc which won’t reliably return the user’s latest subscription;
switch to using the compound index on (userId, startedAt) and order by startedAt
descending when querying for a user's latest subscription. Update the query to
filter by userId, use the compound index (userId, startedAt), order by startedAt
in descending order and then take the first result, and ensure the compound
index exists in the schema if not already defined.


const hasActiveSubscription = subscription?.status === "active";
Expand Down Expand Up @@ -261,6 +262,7 @@ export const fetchUserSubscription = query({
const subscription = await ctx.db
.query("subscriptions")
.withIndex("userId", (q) => q.eq("userId", user.tokenIdentifier))
.order("desc") // Order by createdAt in descending order
.first();
Comment on lines +265 to 266
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Same ordering issue — ensure you truly fetch the latest subscription

Use the (userId, startedAt) compound index and order desc to select the most recent subscription.

Apply this change:

-      .withIndex("userId", (q) => q.eq("userId", user.tokenIdentifier))
-      .order("desc")  // Order by createdAt in descending order
+      .withIndex("by_userId_startedAt", (q) => q.eq("userId", user.tokenIdentifier))
+      .order("desc")  // Latest by startedAt
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.order("desc") // Order by createdAt in descending order
.first();
.withIndex("by_userId_startedAt", (q) => q.eq("userId", user.tokenIdentifier))
.order("desc") // Latest by startedAt
.first();
🤖 Prompt for AI Agents
In convex/subscriptions.ts around lines 265-266, the query currently orders by
createdAt and may not reliably return the latest subscription; change the query
to use the (userId, startedAt) compound index, order by startedAt descending and
keep the single-row fetch (first/limit 1) so it reliably returns the most recent
subscription for the user.


return subscription;
Expand Down Expand Up @@ -328,6 +330,7 @@ export const handleWebhookEvent = mutation({

if (existingSub) {
await ctx.db.patch(existingSub._id, {
polarPriceId: args.body.data.price_id,
amount: args.body.data.amount,
status: args.body.data.status,
currentPeriodStart: new Date(
Expand Down