Skip to main content

Actions

Example: GIPHY Chat

Convex query and mutation functions are great at reading and writing data in Convex, but sometimes you also need to interact with third party services, such as processing a payment on Stripe.

In such cases, you should use another type of Convex function called Action. Actions run in a more permissive environment (Node.js) and can execute arbitrary JavaScript code, including non-deterministic logic. They can interact with the database indirectly by calling queries and mutations.

Defining actions

Actions are defined similar to other Convex functions. They need to be exported from files inside the convex/actions folder and wrapped in action. They can be invoked using the useAction React hook. Here's an example:

convex/actions/file_name.ts
export default action(...);
App.tsx
// Call "folder_name/file_name.ts"'s default export.
useQuery("folder_name/file_name");

Unlike the rest of convex/ folder the convex/actions subfolder is executed in a Node.js environment and can use any compatible libraries.

Interacting with the Database

When the action, is called on the server, it receives ActionCtx as first argument. It can call queries and mutations via the query and mutation properties. Here is an example action that first calls GIPHY service to fetch a gif URL, and then calls a mutation that writes that URL to Convex as a chat message:

api/actions/post-gif.ts
import fetch from "node-fetch";
import { Id } from "../_generated/dataModel";
import { action } from "../_generated/server";

function giphyUrl(query: string) {
return (
"https://api.giphy.com/v1/gifs/translate?api_key=" +
process.env.GIPHY_KEY +
"&s=" +
encodeURIComponent(query)
);
}

// Post a GIF chat message corresponding to the query string.
export default action(
async ({ mutation }, channel: Id<"channels">, query: string) => {
// Fetch GIF url from GIPHY.
const data = await fetch(giphyUrl(query));
const json = await data.json();
const gif_embed_url = json.data.embed_url;

// Write GIF url to Convex.
await mutation("sendMessage", channel, "giphy", gif_embed_url);
}
);

Note that the GIPHY app key is stored in an environment variable which can be set from your dashboard.

Auth

Another property of the ActionCtx is the auth property, which can be used to check client authentication. The auth property is also present in any queries and mutations called from the action.

Error handling

Unlike queries and mutations, actions may have side-effects and therefore can't be automatically retried by Convex when errors occur. For example say your action calls Stripe to send a customer invoice. If it fails part-way through, Convex has no way of knowing if the invoice was already sent and can't safely retry the action. It is responsibility of the caller to handle errors raised by actions and retry if appropriate.

A common developer error is to forget to await all promises created within the actions. Async tasks still running when the function returns might or might not complete and achieve the desired side effect. In addition, since the Node.js execution environment might be reused between action calls, dangling promises might result in errors in subsequent action invocations.