Doogibo
Doogibo
CCConvex Community
Created by Noah on 4/24/2025 in #support-community
Typescript performance issues with caused by Convex types
Yes I deal with copilot fighting with my TS Intellisense. It's a huge pain. In VSCode they do have an aesthetic difference so I can tell which is which, but the timing (copilot is faster at this point, but wrong half the time) throws me off. Do you mean the Go rewrite? I'm kind of hoping to properly resolve as many of the issues I can before that .... hides them all. 😛
10 replies
CCConvex Community
Created by Noah on 4/24/2025 in #support-community
Typescript performance issues with caused by Convex types
We are experiencing the same thing, and trying to figure out the best way to resolve. It kind of crept up on us and now it's very slow. Typescript Intellisense can take quite some time which hurts momentum. If you haven't already seen this: https://github.com/microsoft/Typescript/wiki/Performance There is a lot to that document and I'm still digesting it, but they have a diagnostic tool you can run before/after your changes to gauge improvements. Also ensure you don't have any circular dependencies as those create unnecessary cycles. A good first step. npx madge --circular --extensions ts,tsx src/ at your convex root. Or at your project root as well as you may have introduced some across application components. skipLibCheck set to true in tsconfig can help, but read the caveat in that doc. From there, I'm still trying to decide the best approach. Know that inference is always slower than explicit typing, for complex types, prefer extended interfaces over combined types. When we started, we used Doc<, Id<, z.infer, no explicit return types basically everywhere. Which I'd prefer to keep, as it's a bit of a pain to explicitly type all of these things as an alternative, and I'm still not sure how much it will boost performance. If using vscode, bump up tsserver memory in your workspace settings: "typescript.tsserver.maxTsServerMemory". Smaller files are better, extract helpers. If using a monorepo, you can extract things to separate packages, introduce a build step for them and import them using package imports (i.e. in pnpm, "@mm/shared": "workspace:*"). Especially for things that won't change very often. But here again you'll have to be careful not to introduce circular dependencies. Be thoughtful about how you organize things. Once you do that, only open and work in smaller scoped workspaces, like /convex, or apps/frontend, whatever. Can anyone from the convex team weigh in? Especially re: the idea of reducing usage of Doc<, Id<, z.infer?
10 replies
CCConvex Community
Created by msy on 4/25/2025 in #support-community
Aggregating by userId and creationTime
We're not utilizing time ranges in either of these, so technically you don't even need the _creationTime key. If you wanted to figure out the highest duration in a week or something, you'd just add duration to your sortKey and you'd gain the ability to do this. And so on. If you widen your keys (adding more fields to them), it gives you additional flexibility, but you have to be extra diligent about bounding your queries in mutations. It also increases the potential for conflicts when writing to your aggregate. To reduce wide keys, you need more aggregates, which can mean writing to multiple aggregates in response to "task" mutations. So you have to strike a balance between fewer aggregates, wider keys, and more aggregates with more specific keys. This is something I'm struggling with in my own project. The article suggests always putting bounds around your reads, especially when a read is inside of a mutation. When you do this, your aggregate query becomes a part of the mutation's read set, and the "wider" the bounds of your query, the greater your risk of conflicts is. Lastly, I don't see that you're writing to your aggregate. You may just have left that part out, but you have to ensure you do this to keep them in sync. The article suggests 3 approaches for this.
7 replies
CCConvex Community
Created by msy on 4/25/2025 in #support-community
Aggregating by userId and creationTime
2. If you need to aggregate across users, etc. get rid of the Namespace and add a sortKey:
export const durationAggregateByAuthorAndTime = new TableAggregate<{
Key: [Id<"users">, number];
DataModel: DataModel;
TableName: "tasks";
}>(components.taskDuration, {
sortKey: (doc) => [doc.authorId, doc._creationTime],
sumValue: (doc) => doc.duration,
});
export const durationAggregateByAuthorAndTime = new TableAggregate<{
Key: [Id<"users">, number];
DataModel: DataModel;
TableName: "tasks";
}>(components.taskDuration, {
sortKey: (doc) => [doc.authorId, doc._creationTime],
sumValue: (doc) => doc.duration,
});
You can do things like:
// Same as before, but less efficient for this case than namespacing
export const totalFocusTimeByUserWithKey = query({
args: {
userId: v.id("users"),
},
handler: async (ctx, args) => {
const totalFocusTime = await durationAggregateByAuthorAndTime.sum(ctx, {
// Use 'prefix' to bound the query to keys starting with the userId
prefix: [args.userId],
});
return totalFocusTime;
},
});
// Same as before, but less efficient for this case than namespacing
export const totalFocusTimeByUserWithKey = query({
args: {
userId: v.id("users"),
},
handler: async (ctx, args) => {
const totalFocusTime = await durationAggregateByAuthorAndTime.sum(ctx, {
// Use 'prefix' to bound the query to keys starting with the userId
prefix: [args.userId],
});
return totalFocusTime;
},
});
and
// Another example: Get the total duration across ALL users (system-wide)
export const totalSystemFocusTime = query({
args: {},
handler: async (ctx) => {
// Calling .sum() without bounds/prefix sums across the entire aggregate
const totalDuration = await durationAggregateByAuthorAndTime.sum(ctx);
return totalDuration;
},
});
// Another example: Get the total duration across ALL users (system-wide)
export const totalSystemFocusTime = query({
args: {},
handler: async (ctx) => {
// Calling .sum() without bounds/prefix sums across the entire aggregate
const totalDuration = await durationAggregateByAuthorAndTime.sum(ctx);
return totalDuration;
},
});
7 replies
CCConvex Community
Created by msy on 4/25/2025 in #support-community
Aggregating by userId and creationTime
Which let's you do things like:
export const totalFocusTimeByUser = query({
args: {
userId: v.id("users"),
},
handler: async (ctx, args) => {
// Optional: Check if user exists
await getDocumentOrThrow(ctx, "users", args.userId);

// Use the 'namespace' option to get the sum for the specific user
const totalFocusTime = await durationAggregateByUser.sum(ctx, {
namespace: args.userId,
});
return totalFocusTime;
},
});
export const totalFocusTimeByUser = query({
args: {
userId: v.id("users"),
},
handler: async (ctx, args) => {
// Optional: Check if user exists
await getDocumentOrThrow(ctx, "users", args.userId);

// Use the 'namespace' option to get the sum for the specific user
const totalFocusTime = await durationAggregateByUser.sum(ctx, {
namespace: args.userId,
});
return totalFocusTime;
},
});
and
export const totalFocusTimeByUserForWeek = query({
args: {
userId: v.id("users"),
weekStartTimestamp: v.number(), // Pass the start timestamp of the week
weekEndTimestamp: v.number(), // Pass the end timestamp of the week
},
handler: async (ctx, args) => {
await getDocumentOrThrow(ctx, "users", args.userId);

// Define the bounds based on _creationTime (the key)
const bounds = {
lower: { key: args.weekStartTimestamp, inclusive: true },
upper: { key: args.weekEndTimestamp, inclusive: false } // exclusive upper bound usually makes sense for time ranges, play around with this
};

// Get the sum within the specified namespace and time bounds
const totalFocusTimeByWeek = await durationAggregateByUser.sum(ctx, {
namespace: args.userId,
bounds: bounds,
});
return totalFocusTimeByWeek;
},
});
export const totalFocusTimeByUserForWeek = query({
args: {
userId: v.id("users"),
weekStartTimestamp: v.number(), // Pass the start timestamp of the week
weekEndTimestamp: v.number(), // Pass the end timestamp of the week
},
handler: async (ctx, args) => {
await getDocumentOrThrow(ctx, "users", args.userId);

// Define the bounds based on _creationTime (the key)
const bounds = {
lower: { key: args.weekStartTimestamp, inclusive: true },
upper: { key: args.weekEndTimestamp, inclusive: false } // exclusive upper bound usually makes sense for time ranges, play around with this
};

// Get the sum within the specified namespace and time bounds
const totalFocusTimeByWeek = await durationAggregateByUser.sum(ctx, {
namespace: args.userId,
bounds: bounds,
});
return totalFocusTimeByWeek;
},
});
7 replies
CCConvex Community
Created by msy on 4/25/2025 in #support-community
Aggregating by userId and creationTime
Hey @msy ! Someone can correct me if I'm wrong, I'm also currently just diving into using aggregates. Definitely recommend reading this article (a few times even) if you haven't already: https://www.convex.dev/components/aggregate, because there are a lot of things you need to understand to a. get the outputs you actually want, and b. not running into performance issues/write conflicts (https://docs.convex.dev/database/advanced/occ) ...also checking out/running their example which also has tests: https://github.com/get-convex/aggregate First, you have to decide on what you really need here. Do you only care about user specific duration, or do you also care about duration information across multiple users/comparisons and ranking between users? 1. If you only care about user specific durations, you can introduce Namespaces, which will improve performance and reduce potential write conflicts (but will not let you aggregate across users). It would look something like this:
export const durationAggregateByUser = new TableAggregate<{
Namespace: Id<"tasks">;
Key: number;
DataModel: DataModel;
TableName: "tasks";
}>(components.taskDuration, {
namespace: (doc) => doc.authorId,
sortKey: (doc) => doc._creationTime,
sumValue: (doc) => doc.duration,
});
export const durationAggregateByUser = new TableAggregate<{
Namespace: Id<"tasks">;
Key: number;
DataModel: DataModel;
TableName: "tasks";
}>(components.taskDuration, {
namespace: (doc) => doc.authorId,
sortKey: (doc) => doc._creationTime,
sumValue: (doc) => doc.duration,
});
7 replies
CCConvex Community
Created by Doogibo on 12/13/2024 in #support-community
VERCEL_URL - dynamic Convex env
Thank you so much everyone!
26 replies
CCConvex Community
Created by Doogibo on 12/13/2024 in #support-community
VERCEL_URL - dynamic Convex env
WOOHOOOOOOOO!!!! :fixed:
26 replies
CCConvex Community
Created by Doogibo on 12/13/2024 in #support-community
VERCEL_URL - dynamic Convex env
Oooooh! Let me give that a shot. 🙂
26 replies
CCConvex Community
Created by Doogibo on 12/13/2024 in #support-community
VERCEL_URL - dynamic Convex env
Let me try deploying again to see, I didn't copy it but I feel like it was the preview one not the prod one.
26 replies
CCConvex Community
Created by Doogibo on 12/13/2024 in #support-community
VERCEL_URL - dynamic Convex env
Oh you said CONVEX_URL, my bad.
26 replies
CCConvex Community
Created by Doogibo on 12/13/2024 in #support-community
VERCEL_URL - dynamic Convex env
&& echo "Deploy key exists: $CONVEX_DEPLOY_KEY" && npx convex env set VERCEL_URL $VERCEL_URL Does print out the key correctly. Interesting
26 replies
CCConvex Community
Created by Doogibo on 12/13/2024 in #support-community
VERCEL_URL - dynamic Convex env
That would be great. Still no dice on my end 😦
26 replies
CCConvex Community
Created by Doogibo on 12/13/2024 in #support-community
VERCEL_URL - dynamic Convex env
@ballingt Well this is actually what we get: Finished running function "system/dummy:seedForPreviewDeployments" ✖ Please set CONVEX_DEPLOY_KEY to a new key which you can find on your Convex dashboard. Which we don't get unless we include the second env set command.
26 replies
CCConvex Community
Created by Doogibo on 12/13/2024 in #support-community
VERCEL_URL - dynamic Convex env
Still struggling. Really want the CLI env set approach to work:
npx convex deploy --cmd 'npm run build' --preview-run 'system/dummy:seedForPreviewDeployments' && npx convex env set VERCEL_URL $VERCEL_URL
npx convex deploy --cmd 'npm run build' --preview-run 'system/dummy:seedForPreviewDeployments' && npx convex env set VERCEL_URL $VERCEL_URL
Thinking it's possibly the "once it's provisioned" piece... don't get any helpful errors in the vercel deploy logs.
26 replies
CCConvex Community
Created by Doogibo on 12/13/2024 in #support-community
VERCEL_URL - dynamic Convex env
Ah yes another solid option!
26 replies
CCConvex Community
Created by Doogibo on 12/13/2024 in #support-community
VERCEL_URL - dynamic Convex env
OK perfect thank you Tom! I will try this. 😄
26 replies
CCConvex Community
Created by Doogibo on 12/13/2024 in #support-community
VERCEL_URL - dynamic Convex env
Yes that's right
26 replies
CCConvex Community
Created by Doogibo on 12/13/2024 in #support-community
VERCEL_URL - dynamic Convex env
Alternatively I guess we could pass it as an argument to our functions from the client, since VERCEL_URL is automatically set there. Easier... thoughts? EDIT: I really don't like this approach actually... would involve passing it down in so many cases (any endpoint that fires a notification action, etc.).
26 replies
CCConvex Community
Created by Doogibo on 11/29/2024 in #support-community
Conditionally Building Queries
Thanks Lee and everyone else. It's fine for now. Could definitely be made nicer, but moving on for now. 🙂
10 replies