zid
zid
CCConvex Community
Created by zid on 9/13/2024 in #support-community
Best pattern for mutation after query
I'm simplifying my use case quite a bit but essentially on page load, I'd like to update the user's stats at specific intervals (week, month, etc). I understand that I can't call a mutation within a query, and I've read another support ticket Mutations Inside Queries where the suggestion was to simply invoke a mutation. This will work but given that a significant portion of the user's view will be dependent on the results from this mutation, this feels a little hacky as I'm incorporating useEffect + some state to fetch the data, while also having to setup a separate query in order to see the data in realtime. I'll also have to add some code to deal with a potential flicker since the order of operations is results from query, then results from mutation. Not a big deal, but curious whether there's a better paradigm?
3 replies
CCConvex Community
Created by zid on 9/5/2024 in #support-community
Cant get to my convex dashboard
No description
7 replies
CCConvex Community
Created by zid on 8/8/2024 in #support-community
latest version of clerk incompatible with convex
@clerk/nextjs: ^5.0.12 // works
@clerk/nextjs: ^5.3.0 // does not work
@clerk/nextjs: ^5.0.12 // works
@clerk/nextjs: ^5.3.0 // does not work
Specifically, npx convex dev gives me
✘ [ERROR] Could not resolve "node:async_hooks"

node_modules/@clerk/nextjs/dist/esm/server/clerkMiddleware.js:1:34:
1 │ import { AsyncLocalStorage } from "node:async_hooks";
╵ ~~~~~~~~~~~~~~~~~~

The package "node:async_hooks" wasn't found on the file system but is built into node. Are you
trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this
error.
✘ [ERROR] Could not resolve "node:async_hooks"

node_modules/@clerk/nextjs/dist/esm/server/clerkMiddleware.js:1:34:
1 │ import { AsyncLocalStorage } from "node:async_hooks";
╵ ~~~~~~~~~~~~~~~~~~

The package "node:async_hooks" wasn't found on the file system but is built into node. Are you
trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this
error.
I plan to switch over to convex auth anyway, but still.
1 replies
CCConvex Community
Created by zid on 7/15/2024 in #support-community
Best practice for large, multi-mutation sequence with convex
I'm trying to create a series of mutation calls to handle the event where the user deletes their account. When a user deletes their account, I need to: - update every thread the user created (recursive scheduled tasks) - update every comment the user created (recursive scheduled tasks) - update user details - update or delete every message the user created (recursive scheduled tasks) Given resource limits for each mutation, i'm guessing invoking 4 separate mutations from the client (rather than inside a single parent mutation) is the way to go here...maybe not.. Should one of these mutations fail, I'll either need to rollback the updates and throw an error or ... I need to think of a better way to handle this altogether. I could also be going about this rather inefficiently...hence me bringing this up here, in case im making an incorect assumption about Convex.
10 replies
CCConvex Community
Created by zid on 7/15/2024 in #support-community
Best practice for testing recursive, potentially costly mutation
I'm nervous as there seems to be no manual kill switch for a recursive mutation call in convex. I've setup basic logic to exit out, however still, I'd appreciate a second look along with some ideas for best practices regarding these costly fns.
const MAX_RECURSION_DEPTH = 20;

export const deactivateThreads = internalMutation({
args: {
cursor: v.optional(v.union(v.string(), v.null())),
userId: v.id("users"),
depth: v.optional(v.number()),
},
handler: async ({ db, scheduler }, { cursor, userId, depth = 0 }) => {
if (depth > MAX_RECURSION_DEPTH) {
throw new Error("Maximum recursion depth exceeded");
}
try {
const user = await db.get(userId);
const savedCursor =
cursor ||
(user?.deactivation?.nextCursor ? user.deactivation.nextCursor : null);

const batchSize = 2;

const { page, continueCursor, isDone, pageStatus } = await db
.query("threads")
.withIndex("by_userId", (q) => q.eq("userId", userId))
.paginate({
numItems: batchSize,
cursor: savedCursor,
});

for (let i = 0; i < page.length; i++) {
const currentThread = page[i];
await db.delete(currentThread._id);
}

if (isDone) {
// await resetCursor(db);
await db.patch(userId, {
deactivation: {
...user.deactivation,
nextCursor: null,
},
});
return;
}

if (!isDone) {

await db.patch(userId, {
deactivation: {
count: user?.deactivation?.count ? user.deactivation.count + 1 : 0,
nextCursor: continueCursor,
},
});

await scheduler.runAfter(0, internal.internalUser.deactivateThreads, {
cursor: continueCursor,
userId: userId,
depth: depth + 1,
});
}
} catch (error) {
throw error;
}
},
});
const MAX_RECURSION_DEPTH = 20;

export const deactivateThreads = internalMutation({
args: {
cursor: v.optional(v.union(v.string(), v.null())),
userId: v.id("users"),
depth: v.optional(v.number()),
},
handler: async ({ db, scheduler }, { cursor, userId, depth = 0 }) => {
if (depth > MAX_RECURSION_DEPTH) {
throw new Error("Maximum recursion depth exceeded");
}
try {
const user = await db.get(userId);
const savedCursor =
cursor ||
(user?.deactivation?.nextCursor ? user.deactivation.nextCursor : null);

const batchSize = 2;

const { page, continueCursor, isDone, pageStatus } = await db
.query("threads")
.withIndex("by_userId", (q) => q.eq("userId", userId))
.paginate({
numItems: batchSize,
cursor: savedCursor,
});

for (let i = 0; i < page.length; i++) {
const currentThread = page[i];
await db.delete(currentThread._id);
}

if (isDone) {
// await resetCursor(db);
await db.patch(userId, {
deactivation: {
...user.deactivation,
nextCursor: null,
},
});
return;
}

if (!isDone) {

await db.patch(userId, {
deactivation: {
count: user?.deactivation?.count ? user.deactivation.count + 1 : 0,
nextCursor: continueCursor,
},
});

await scheduler.runAfter(0, internal.internalUser.deactivateThreads, {
cursor: continueCursor,
userId: userId,
depth: depth + 1,
});
}
} catch (error) {
throw error;
}
},
});
3 replies
CCConvex Community
Created by zid on 6/6/2024 in #support-community
Runtime broken, out of nowhere i'm getting a build error from running convex.dev
The esbuild loader for this file is currently set to "js" but it must be set to "jsx" to be able
to parse JSX syntax. You can use "loader: { '.js': 'jsx' }" to do that.

✘ [ERROR] The JSX syntax extension is not currently enabled
The esbuild loader for this file is currently set to "js" but it must be set to "jsx" to be able
to parse JSX syntax. You can use "loader: { '.js': 'jsx' }" to do that.

✘ [ERROR] The JSX syntax extension is not currently enabled
Please assist
28 replies
CCConvex Community
Created by zid on 5/17/2024 in #support-community
Clerk + Convex: Can't use fetch() in queries and mutations. Please consider using an action
Inside my convex folder, i have a mutation that is creating a user, and if this fails, I want to delete the user from Clerk. I cant use useAction inside this context, so I tried importing it directly which is seemingly okay. I'm using an action as logs suggested, but obviously i'm misunderstanding something. // somewhere inside convex, inside a mutation
import { deleteClerkUser } from "./clerk";

try {
const userId = await createUser({
db,
identity,
});
return userId;
} catch (error) {
// if theres an error, delete clerk user
console.log("clerk user id of deleting user", identity.subject);
try {
await deleteClerkUser({
clerkUserId: identity.subject,
});
} catch (err) {
console.log("deleteClerkUser", err);
throw err;
}
console.log("createUser", error);
throw error;
}
import { deleteClerkUser } from "./clerk";

try {
const userId = await createUser({
db,
identity,
});
return userId;
} catch (error) {
// if theres an error, delete clerk user
console.log("clerk user id of deleting user", identity.subject);
try {
await deleteClerkUser({
clerkUserId: identity.subject,
});
} catch (err) {
console.log("deleteClerkUser", err);
throw err;
}
console.log("createUser", error);
throw error;
}
// convex/clerk.js
"use node";
import { clerkClient } from "@clerk/nextjs/server";

export const deleteClerkUser = action({
args: { clerkUserId: v.string() },
handler: async ({ clerkUserId }) => {
try {
await clerkClient.users.deleteUser(clerkUserId);
} catch (err) {
console.error(err);
return { success: false, error: err.message };
}
},
get handler() {
return this._handler;
},
set handler(value) {
this._handler = value;
},
});
"use node";
import { clerkClient } from "@clerk/nextjs/server";

export const deleteClerkUser = action({
args: { clerkUserId: v.string() },
handler: async ({ clerkUserId }) => {
try {
await clerkClient.users.deleteUser(clerkUserId);
} catch (err) {
console.error(err);
return { success: false, error: err.message };
}
},
get handler() {
return this._handler;
},
set handler(value) {
this._handler = value;
},
});
Error message:
[
{
code: 'unexpected_error',
message: 'Can\'t use fetch() in queries and mutations. Please consider using an action. See https://docs.convex.dev/functions/actions for more details.'
}
]
[
{
code: 'unexpected_error',
message: 'Can\'t use fetch() in queries and mutations. Please consider using an action. See https://docs.convex.dev/functions/actions for more details.'
}
]
65 replies
CCConvex Community
Created by zid on 5/14/2024 in #support-community
400 Bad Request: TooManyIndexes?
The error says I have more than 32 indexes on a table when I only have 29?
3 replies
CCConvex Community
Created by zid on 5/14/2024 in #support-community
isAuthenticated is always false
I started a new project by copying all contents from another project. I swapped all credentials and env variables to the new ones; the new ones from convex and clerk. convex verbose logs show all green as well as network logs. also did not change the jwt template name "convex" oh and I successfully create and get the user object from clerk for some reason isAuthenticated is always false. for the life of me i cant figure out what's the issue. i'm about the start the new project from scratch rather than beginning with the copy. Hoping this doesnt come to that...requesting assistance, and thank you in advance.
29 replies
CCConvex Community
Created by zid on 3/20/2024 in #support-community
During development, my websock connection keeps closing?
WebSocket error: undefined
web_socket_manager.js:72 WebSocket closed with code 1006
web_socket_manager.js:118 Attempting reconnect in 23356.7457759501ms
WebSocket error: undefined
web_socket_manager.js:72 WebSocket closed with code 1006
web_socket_manager.js:118 Attempting reconnect in 23356.7457759501ms
3 replies
CCConvex Community
Created by zid on 2/29/2024 in #support-community
Curious how convex team architects around their userId when using Clerk
Im playing around with Next 13/14 (my app is currently in 12). In doing so, im seeing if i can more efficiently fetch userId. For context, my setup is Convex + Clerk + NextJS. Thus far (Next 12), I've always used the userId generated by convex from my users table. But given the fact that I can fetch Clerk's userId on the server, I'm trying to think of the pros/cons of trying to use Clerks userId instead. The main drawback is switching from the db.get(userId) to db.query...unique(), which I think is technically more costly even with an index applied. Then there's convex's prefetching api, where I can gain access to convex serverside. To my understanding, the primary drawback to this is that we're. losing out on convex core guarantees, but if it's just the userId we're fetching, then I assume this should be fine. All of this to say, given that I'm venturing into a new paradigm with Next 13/14, I would love the opinions from the Convex team on how they handle this and perhaps how to think about this in general. Thank you in advance as always
16 replies
CCConvex Community
Created by zid on 2/21/2024 in #support-community
The InvalidCursor error happens if the query's index range changes between requests
Wanting to confirm something. The InvalidCursor error ( InvalidCursor: Tried to run a query starting from a cursor, but it looks like this cursor is from a different query.) happens if the query's index range changes between requests, and thus it is not possible/viable to setup multiple query definitions/statements if (x) query.withIndex(by_1), else if(y) withIndex(by_2) inside a single query function? Is the best/only way then to separate each query into its own query function?
8 replies
CCConvex Community
Created by zid on 2/14/2024 in #support-community
Using convex server-side (nextJS api routes)
Having some trouble getting convex to work inside NextJS api routes. This is inside /api/fn.js The mutation fn is defined but the fn is not executing..
import { mutation } from "@/convex/_generated/server";

// ...
await mutation(async ({ db }, { arg1, arg2 }) => {

await db.patch(userId, {
// ...
});

});
import { mutation } from "@/convex/_generated/server";

// ...
await mutation(async ({ db }, { arg1, arg2 }) => {

await db.patch(userId, {
// ...
});

});
9 replies
CCConvex Community
Created by zid on 2/3/2024 in #support-community
Confirmation regarding filtering on paginated queries
Just wanted to confirm how filtering works in a very specific scenario. When 1. not using an index and 2. using a paginated query, will applying a filter on this query perform a full table scan or just on the current page?
4 replies
CCConvex Community
Created by zid on 1/27/2024 in #support-community
Complex queries based off of items in an array
I'd like to be able to: .widthIndex("by_userId_primarySkills_secondarySkills", q => q.includes("primarySkills", arrayItem)) I have some complex query requirements and the above would help immensely! But perhaps there's another way to efficiently accomplish this? I have a users table and a separate table skills Each skills document has a type field that can hold one of three types primary, secondary, and lookingFor. Each user can have up to 3 primary types/docs, 3 secondary types/docs, and 6 lookingFor types/docs. With this schema, it's challenging to write a complex query without a lot of application logic and costly ($$) additional reads. But if I could denormalize the data for users to have users.primarySkills where primarySkills is an array, and if I could query at the array item level, that would be a seamless solution
13 replies
CCConvex Community
Created by zid on 1/25/2024 in #support-community
Handling errors from convex
Hey Team, is there a better way to handle/throw convex errors to the client? I have a particular setup for one part of my app where it would be very efficient if I could simply throw the error directly from the server. Here's the way I'm handling this now
const handleUpdateSkill = async ({ type, skill }) => {
try {
await updateSkill({
userId,
type,
skill,
});
} catch (error) {
let errorMessage = error.message || "Cannot add more skills";

// First, remove the known prefix if it's there
const prefixToRemove = "[CONVEX M(arsenal:updateSkill)] Uncaught Error: ";
errorMessage = errorMessage.replace(prefixToRemove, "");

// Now, remove any trailing details after "at handler"
errorMessage = errorMessage.split("\n")[0];

toast.error(errorMessage);
}
};
const handleUpdateSkill = async ({ type, skill }) => {
try {
await updateSkill({
userId,
type,
skill,
});
} catch (error) {
let errorMessage = error.message || "Cannot add more skills";

// First, remove the known prefix if it's there
const prefixToRemove = "[CONVEX M(arsenal:updateSkill)] Uncaught Error: ";
errorMessage = errorMessage.replace(prefixToRemove, "");

// Now, remove any trailing details after "at handler"
errorMessage = errorMessage.split("\n")[0];

toast.error(errorMessage);
}
};
8 replies
CCConvex Community
Created by zid on 1/18/2024 in #support-community
Pagination features
For paginated queries, rather than concatenating each batch, will sorting the entire returned list server-side ever be supported? And/or it would be great to be able to apply custom/advanced sorting algorithms once withIndex's have been applied. Currently, I have my own sorting algorithm applied to just the batch once it reaches the client, to which then I recalculate for every concatenated batch.
25 replies
CCConvex Community
Created by zid on 1/16/2024 in #support-community
.neq?
In the docs I see q.neq(l, r) l !== r is a valid filter operator, but it doesnt seem to be working? Error: [CONVEX Q(user:getThreads)] Uncaught TypeError: q.eq(...).eq(...).neq is not a function
.withIndex(
`by_districtsId_status_userId_${filter}`,
(q) =>
q
.eq("districtsId", districtsId)
.eq("status", "visible")
.neq("userId", userId)
)
.withIndex(
`by_districtsId_status_userId_${filter}`,
(q) =>
q
.eq("districtsId", districtsId)
.eq("status", "visible")
.neq("userId", userId)
)
9 replies
CCConvex Community
Created by zid on 1/15/2024 in #support-community
multiple pagination queries in a single function
i imagine this to be uncommon scenario, but came across a situation where having the ability to setup 2 pagination queries inside a single function wouldve been great. my scenario is that i have 2 tables, threads and comments, where they each have a status property that can hold the following values hidden and visible. What id like to be able to do is fetch all the hidden threads and comments, and return a single list [...threads, ...comments]. Another way to handle this is to create a dedicated table hiddenPosts for example, but feels like more of a workaround. Currently, I'm using two separate pagination queries, then using a useEffect to concatenate/spread them into a single result using local state. Curious if this will ever be supported?
3 replies
CCConvex Community
Created by zid on 1/14/2024 in #support-community
✖ Error: Unable to build indexes and run schema validation on...
While running convex npx convex dev, I'm starting to get the following error on a seemingly frequent basis.
AxiosError: maxContentLength size of -1 exceeded
Failed due to network error, retrying in 262.72ms...
✖ Error: Unable to build indexes and run schema validation on [instance url]
AxiosError: maxContentLength size of -1 exceeded
Failed due to network error, retrying in 262.72ms...
✖ Error: Unable to build indexes and run schema validation on [instance url]
I see that it logs a network error, but does it have anything to do with how ive setup my schema? This is purely from speculation, but I'm wondering if it's because I have some long-winded indexes (char length). Not sure what the limitations are around naming indexes (aside from accept only letters, numbers, and underscores).
8 replies