Skip to main content

Actions

Actions can call third party services to do things such as processing a payment with Stripe. They can be run in Convex's JavaScript environment or in Node.js. They can interact with the database indirectly by calling queries and mutations.

Example: GIPHY Action

Action names

Actions follow the same naming rules as queries, see Query names.

The action constructor

To declare an action in Convex you use the action constructor function and pass it an implementation function:

convex/myAction.js
import { action } from "./_generated/server";

export default action(() => {
// implementation goes here
});

Security Note - By default action in Convex is public. If your mutation does not need to be used by clients, use internalAction instead. You should also consider argument validation and/or authentication.

Action arguments and responses

Action arguments and responses follow the same rules as mutations:

convex/myAction.js
import { action } from "./_generated/server";

export default action((_, { a, b }) => {
// do something with `a` and `b`
});

The first argument to the implementation function is reserved for the action context.

Action context

The action constructor enables interacting with the database, and other Convex features by passing an ActionCtx object to the implementation function as the first argument:

convex/myAction.js
import { action } from "./_generated/server";

export default action((actionCtx, { a, b }) => {
// Do something with `actionCtx`
});

Which part of that action context is used depends on what your action needs to do:

  • To read data from the database use the runQuery field, and call a query that performs the read:

    convex/myAction.js
    export default action(async ({ runQuery }, { a, b }) => {
    const data = await runQuery("readData", { a });
    // do something with `data`
    });

    Use an internal query when you want to prevent users from calling the query directly.

    It's often convenient to define actions and queries in the same file:

    convex/myAction.js
    export default action(async ({ runMutation }, { a, b }) => {
    const data = await runQuery("myAction:readData", { a });
    // do something with `data`
    });

    export const readData = internalQuery(async ({ db }, { a }) => {
    // Read from `db` here
    });
  • To write data to the database use the runMutation field, and call a mutation that performs the write:

    convex/myAction.js
    export default action(async ({ runMutation }, { a, b }) => {
    await runMutation("writeData", { a });
    // do something else
    });

    Use an internal mutation when you want to prevent users from calling the mutation directly.

    As with queries, it's often convenient to define actions and mutations in the same file.

  • To generate upload URLs for storing files use the storage field. Read on about File Storage.

  • To check user authentication use the auth field. Auth is propagated automatically when calling queries and mutations from the action. Read on about Authentication.

  • To schedule functions to run in the future, use the scheduler field. Read on about Scheduled Functions.

Calling third-party APIs and using NPM packages

Actions can run in Convex's custom JavaScript environment or in Node.js.

By default, actions run in Convex's environment. This environment supports fetch, so actions that simply want to call a third-party API using fetch can be run in this environment:

convex/myAction.js
import { action } from "./_generated/server";

export default action(async _ => {
const data = await fetch("https://api.thirdpartyservice.com");
// do something with data
});

Actions running in Convex's environment are faster compared to Node.js, since they don't require extra time to start up before running your action (cold starts). They can also be defined in the same file as other Convex functions.

Actions using NPM packages or Node.js APIs can be configured to run in Node.js by adding the "use node" directive at the top of the file. Note that other Convex functions cannot be defined in files with the "use node"; directive.

convex/myAction.js
"use node";
import { action } from "./_generated/server";
import SomeNpmPackage from "some-npm-package";

export default action(async _ => {
// do something with SomeNpmPackage
});

Calling actions from clients

To call an action from React use the generated useAction hook:

src/MyApp.jsx
import { useAction } from "../convex/_generated/react";

export function MyApp() {
const performMyAction = useAction("myAction");
const handleClick = () => {
performMyAction({ a, b });
};
// pass `handleClick` to a button
// ...
}

See Client Libraries for all the ways actions can be called from different clients.

Unlike mutations, actions from a single client are parallelized. Each action will be executed as soon as it reaches the server (even if other actions and mutations from the same client are running).

If your app relies on actions running after other actions or mutations make sure to only trigger the action after the relevant previous function completes.

Limits

Actions time out after 2 minutes and they can use up to 512MB of memory. Please contact us if you have a use case that requires higher limits.

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 the HTTP request fails, Convex has no way of knowing if the invoice was already sent. Like in normal backend code, it is the responsibility of the caller to handle errors raised by actions and retry the action call if appropriate.

Make sure to await all promises created within an action. Async tasks still running when the function returns might or might not complete. In addition, since the Node.js execution environment might be reused between action calls, dangling promises might result in errors in subsequent action invocations.