José Alvarez
José Alvarez3mo ago

Can actions actually return a value?

The documentation is not very clear about whether actions can have a return value or not. If they can't, I'd apprecaite it if the documentation could acknowledge this clearly. If they can, I'd appreciate a clear example showcasing how to do this. For example, I would like to have this kind of action.
export const createThread = internalAction({
handler: async () => {
const apiKey = process.env.OPENAI_API_KEY!;
const openai = new OpenAI({ apiKey });

const thread = await openai.beta.threads.create();

return thread.id; // Also, could I return the thread object?
},
});
export const createThread = internalAction({
handler: async () => {
const apiKey = process.env.OPENAI_API_KEY!;
const openai = new OpenAI({ apiKey });

const thread = await openai.beta.threads.create();

return thread.id; // Also, could I return the thread object?
},
});
I am not interested in calling this action in the front-end (hence internalAction). I've seen in the demos on GitHub (https://github.com/get-convex/convex-demos) examples of actions being called in the front-end (https://github.com/get-convex/convex-demos/tree/main/vector-search) to get a return value, but not examples of actions returning something in the backend. Indeed, my seemingly only option to run an action in the backend is with the scheduler:
const threadId: string = await ctx.scheduler.runAfter(
0,
internal.openai.createThread
);
const threadId: string = await ctx.scheduler.runAfter(
0,
internal.openai.createThread
);
The above schedules the action to run in the future, which is not what I want.
I want to actually await the action and get its return value before executing the rest of the backend code (in this case, a mutation), is there a way to do this? I also tried to call openai.beta.threads, but the linter complains that this expression it's not callable. Originally I tried to just use a normal function, or what it's equivalent, just call openai.beta.threads.create() inside of my mutation, but I got this:
Uncaught Error: Uncaught Error: Can't use setTimeout in queries and mutations. Please consider using an action. See https://docs.convex.dev/functions/actions for more details.
at fetchWithTimeout [as fetchWithTimeout] (../node_modules/openai/src/core.ts:556:11)
Uncaught Error: Uncaught Error: Can't use setTimeout in queries and mutations. Please consider using an action. See https://docs.convex.dev/functions/actions for more details.
at fetchWithTimeout [as fetchWithTimeout] (../node_modules/openai/src/core.ts:556:11)
Thank you!
GitHub
GitHub - get-convex/convex-demos: Demo apps built on Convex.
Demo apps built on Convex. Contribute to get-convex/convex-demos development by creating an account on GitHub.
GitHub
convex-demos/vector-search at main · get-convex/convex-demos
Demo apps built on Convex. Contribute to get-convex/convex-demos development by creating an account on GitHub.
3 Replies
Convex Bot
Convex Bot3mo ago
Thanks for posting in <#1088161997662724167>. Reminder: If you have a Convex Pro account, use the Convex Dashboard to file support tickets. - Provide context: What are you trying to achieve, what is the end-user interaction, what are you seeing? (full error message, command output, etc.) - Use search.convex.dev to search Docs, Stack, and Discord all at once. - Additionally, you can post your questions in the Convex Community's <#1228095053885476985> channel to receive a response from AI. - Avoid tagging staff unless specifically instructed. Thank you!
lee
lee3mo ago
hi! great questions, and not readily answered by docs; we'll work on that. actions can return values. the values can be JSON-serializable objects or a few other types. notably complex streams and classes can't be returned: https://docs.convex.dev/database/types#convex-values actions can run most code, including calling openai apis. but there are restrictions on what a mutation or query can call synchronously. because they run as transactions, they cannot have side effects or take a long time (hence no setTimeout). And mutations can't call actions synchronously, which might take a while or be nondeterministic. https://docs.convex.dev/functions/runtimes#restrictions-on-queries-and-mutations action return values are only useful when called synchronously, which can happen if called from another action or an httpAction or with useAction from a client. when scheduled with ctx.scheduler.runAfter or with a cron, the return value isn't useful. Since a mutation can't call an action synchronously, it can't use the action's return value. so to solve your use-case, i imagine you want to run a mutation that schedules an action, and the action calls ctx.runMutation at the end, to store its return value and complete the workflow.
Runtimes | Convex Developer Hub
Convex functions can run in two runtimes:
Data Types | Convex Developer Hub
All Convex documents are defined as Javascript objects. These objects can have
José Alvarez
José AlvarezOP3mo ago
thanks! that's the flow we arrived at when I discussed it with the support team via email yesterday: initial mutation: create the record schedule the background action inside the action: call apis, do anything you want call a mutation to save the results back to the original record The app is subscribed to the record via a query, so it automatically updates as you proceed.

Did you find this page helpful?