Web Dev Cody
Web Dev Cody•14mo ago

Recommended way to build up file structure?

one approach I've been taking is to group my convex directory into related data models, and create a queries.ts, mutations.ts, and actions.ts. Is there any recommended approach that others like doing for larger convex projects?
No description
17 Replies
jamwt
jamwt•14mo ago
@Web Dev Cody this is pretty common. some folks also put the model in a model.ts file with the queries/mutations together meaning, plans would just be plans.ts but as a codebase grows, some models get broken up further into directories, and sometimes queries and mutations are separated like this
Michal Srb
Michal Srb•14mo ago
My ideal would be a semantic tree, and I wouldn't worry about splitting read/write/action. So plans > sections.ts, and when I want to break up sections I would turn it into a directory. This only really works before going public, afterwards you'd want to maintain backwards compatibility. So another angle is to think of a fairly future proof name for a function (perhaps building out the directory structure even before the complexity warrants it).
ian
ian•14mo ago
On the backwards compatibility front, for API breaking changes it's sometimes easier to change the function to a new one (and keep the old deprecated one with the old API until clients drop off), since you'll only get new clients to the new endpoint. an extreme example of this is to make a v2 directory for a new API, and pass around a reference to api.v2 to all functions, leaving the original functions there until you can sunset them.
export const latest = api.v2;
...
useQuery(latest.module.fn)
export const latest = api.v2;
...
useQuery(latest.module.fn)
Then when you change latest to a new version, you will get type errors everywhere that your new API breaks the old types. This is a concept only right now - haven't actually done it. It's nice that you avoid breaking old clients b/c typescript forces you to fix older code when the data model changes, so long as you're careful not to change the arguments / return types while you're refactoring.
Web Dev Cody
Web Dev CodyOP•14mo ago
@ian @Michal Srb so, you mention that you'd use a semantic tree, but my main complaint is the moment I need 'use node', I can no longer follow a semantic tree so I'm basically forced to split by technology I guess I still just need to find what works best for my thought process, because 'use node' forces my hand to pull out mutation the moment a single action needs node functionality
jamwt
jamwt•14mo ago
yep, right now this part is admittedly awkward in a perfect world we'd be able to annotate individual functions or whatever as use node but we're relying on existing toolchains/bundlers etc and this is hard to achieve so far. the hope is over time more and more stuff doens't require 'use node' b/c it has a lot of other tradeoffs but some things will always need it, and we don't love right now that you need a separate module just for that
Web Dev Cody
Web Dev CodyOP•14mo ago
one example, maybe you have a better solution, I just wanted to generate a randomUUID using the node's crypto library is there a way to do that inside a mutation? without needing the node library? like to generate an random API key or crypto.randomBytes(16).toString('hex')
jamwt
jamwt•14mo ago
we do support the web crypto api in our runtime
Web Dev Cody
Web Dev CodyOP•14mo ago
I'm guessing using crypto is much more secure than Math.random() oh
jamwt
jamwt•14mo ago
I think @sshader had a pretty nice list of what's supported / unsupported right now, but I can't find it in the docs currently 😕
Web Dev Cody
Web Dev CodyOP•14mo ago
I guess I should read this a bit
jamwt
jamwt•14mo ago
ah yeah, I should have expanded
Michal Srb
Michal Srb•14mo ago
Yeah @Web Dev Cody, I pretty much never have to use Node these days. Any time you're forced to use Node, let us know.
Web Dev Cody
Web Dev CodyOP•14mo ago
so is crypto just a global thing we have access to? let me just test real quick, not sure why I'm asking
jamwt
jamwt•14mo ago
looks like the randomUUID method might be the thing
Web Dev Cody
Web Dev CodyOP•14mo ago
yeah this works fine
import { mutation } from "../_generated/server";
import { v } from "convex/values";

export const createKey = mutation({
args: {
label: v.string(),
},
handler: async (ctx, args) => {
const userId = (await ctx.auth.getUserIdentity())?.subject;

if (!userId) {
throw new Error("you must be logged in to create a thumbnail");
}

await ctx.db.insert("keys", {
userId,
key: crypto.randomUUID(),
label: args.label,
});
},
});
import { mutation } from "../_generated/server";
import { v } from "convex/values";

export const createKey = mutation({
args: {
label: v.string(),
},
handler: async (ctx, args) => {
const userId = (await ctx.auth.getUserIdentity())?.subject;

if (!userId) {
throw new Error("you must be logged in to create a thumbnail");
}

await ctx.db.insert("keys", {
userId,
key: crypto.randomUUID(),
label: args.label,
});
},
});
I guess I wasn't aware it's just a global 😆
jamwt
jamwt•14mo ago
cool! yeah, our runtime is more like the browser runtime than node.js (or a big cloud's "edge" runtime, but same idea) so often the API you're looking for is more in that documentation culture than node's

Did you find this page helpful?