saito200
saito200
CCConvex Community
Created by saito200 on 2/3/2024 in #support-community
Auth with chained actions and mutations?
I am finding it much simpler if I just do not use the scheduler at all and run actions directly from the client tbh, and only go out of the way to use the scheduler if I expect lots of reads and writes
10 replies
CCConvex Community
Created by saito200 on 2/3/2024 in #support-community
Auth with chained actions and mutations?
@ian reusability. For example if I am reusing a query that gets the current user to filter records by user id
10 replies
CCConvex Community
Created by saito200 on 2/3/2024 in #support-community
Auth with chained actions and mutations?
I find if I simply call the processRaw action from the client, everything works. Is it that when using the scheduler, the user auth is lost somehow?
10 replies
CCConvex Community
Created by saito200 on 2/3/2024 in #support-community
Auth with chained actions and mutations?
export async function getAuthedUser(ctx: QueryCtx | MutationCtx) {
const { tokenIdentifier } = await authGuard(ctx);

const user = await ctx.db
.query("users")
.withIndex("byClerkId", (q) => q.eq("clerkId", tokenIdentifier))
.unique();

if (user === null) {
throw new Error("User not found");
}

return user;
}
export async function getAuthedUser(ctx: QueryCtx | MutationCtx) {
const { tokenIdentifier } = await authGuard(ctx);

const user = await ctx.db
.query("users")
.withIndex("byClerkId", (q) => q.eq("clerkId", tokenIdentifier))
.unique();

if (user === null) {
throw new Error("User not found");
}

return user;
}
10 replies
CCConvex Community
Created by saito200 on 2/3/2024 in #support-community
Auth with chained actions and mutations?
This is an example of pseudocode:
// 1. client calls this mutation
export const processRecords = mutation({
args: {
rawInput: v.string()
},
handler: async (ctx, { rawInput }) => {
await ctx.scheduler.runAfter(0, internal.service.processRaw, { rawInput })
}
})

// 2. Mutation calls this action, which needs to run a bunch of actions and mutations in order. I'm not using the scheduler here because each needs to wait for the previous to finish (is this correct?)
export const processRaw = internalAction({
args: {
rawInput: v.string(),
},
handler: async (ctx, { rawInput }) => {
const parsedInputIds = await ctx.runAction(internal.service.parseRaw, { rawInput })
// ... other actions that need to run after the above is done
}
})

// 3. this action gets some API data and runs a mutation directly
export const parseRaw = internalAction({
args: {
rawInput: v.string(),
},
handler: async (ctx, { rawInput }) => {
const response = await fetchSomeApi(rawInput)

const newIds = await Promise.all(response.records.map((body) => {
return ctx.runMutation(api.service.create, { body })
}))

return newIds
}
})

// 4. this public mutation is called and errors out
export const create = mutation({
args: {
body: v.string(),
},
handler: async (ctx, { body }) => {
const user = await getAuthedUser(ctx)
return await ctx.db.insert("ideas", {
body,
userId: user._id
})
},
});

export async function authGuard(ctx: QueryCtx | ActionCtx | MutationCtx) {
const identity = await ctx.auth.getUserIdentity();
if (identity === null) {
throw new Error("Unauthenticated server call"); // this error is triggering, for some reason (but the user is authed)
}

return identity;
}
// 1. client calls this mutation
export const processRecords = mutation({
args: {
rawInput: v.string()
},
handler: async (ctx, { rawInput }) => {
await ctx.scheduler.runAfter(0, internal.service.processRaw, { rawInput })
}
})

// 2. Mutation calls this action, which needs to run a bunch of actions and mutations in order. I'm not using the scheduler here because each needs to wait for the previous to finish (is this correct?)
export const processRaw = internalAction({
args: {
rawInput: v.string(),
},
handler: async (ctx, { rawInput }) => {
const parsedInputIds = await ctx.runAction(internal.service.parseRaw, { rawInput })
// ... other actions that need to run after the above is done
}
})

// 3. this action gets some API data and runs a mutation directly
export const parseRaw = internalAction({
args: {
rawInput: v.string(),
},
handler: async (ctx, { rawInput }) => {
const response = await fetchSomeApi(rawInput)

const newIds = await Promise.all(response.records.map((body) => {
return ctx.runMutation(api.service.create, { body })
}))

return newIds
}
})

// 4. this public mutation is called and errors out
export const create = mutation({
args: {
body: v.string(),
},
handler: async (ctx, { body }) => {
const user = await getAuthedUser(ctx)
return await ctx.db.insert("ideas", {
body,
userId: user._id
})
},
});

export async function authGuard(ctx: QueryCtx | ActionCtx | MutationCtx) {
const identity = await ctx.auth.getUserIdentity();
if (identity === null) {
throw new Error("Unauthenticated server call"); // this error is triggering, for some reason (but the user is authed)
}

return identity;
}
(continue)
10 replies
CCConvex Community
Created by saito200 on 2/1/2024 in #support-community
How to use the result of a scheduled action?
the docs say to avoid calling an action directly from the client, and instead calling a mutation that schedules an action In my case then it would be: client -> public mutation -> run query to get a doc -> run scheduled action -> call third party API -> call internal mutation that writes to DB it honestly feels a bit overcomplicated, I am doing a refactoring where before I simply called an action from the client that did everything
8 replies
CCConvex Community
Created by whoami on 5/7/2023 in #support-community
Supporting cascades delete in convex?
I'm also trying to figure out the way to cascade delete records, e.g. the simplest case would be author and book tables, and if an author is deleted, all books that have a matching authorId would be deleted too In my case I have multiple cascading levels though I can do it with mutations that delete the author and then trigger mutations to delete the orphans I could also do it with cron jobs that inspect the DB and clean up the orphans I have the feeling that the first option would be ok, but I would like to know what is the "intended way" that has the "convex seal of approval", if any
6 replies