#Anti pattern - actions

13 messages · Page 1 of 1 (latest)

wide stag
#

I need to call an API after a certain event on my front end happens

So instead of calling the API from my fro end, to keep my API endpoint private, I was planning on simply creating an action and calling it from my front end.

I don’t expect any output I just want to call it in the background and I don’t care the result

I think creating a mutation is not a good idea because it’s an extra step and I don’t need to save any data

Is this possible? I want to keep things simple

wise graniteBOT
#

Hi @wide stag. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

Calling a Convex action directly from your frontend to trigger an API call is technically possible, but it is generally considered an anti-pattern in Convex. Here’s why:

  • Security and Best Practices: Actions are meant for non-deterministic operations like calling third-party APIs, but calling them directly from the client exposes them as public endpoints. This can lead to security issues, as anyone could trigger the action, potentially abusing your backend or third-party services. Convex recommends keeping sensitive or side-effectful operations private by not exposing them directly to the client.

  • Recommended Pattern: The best practice is to have your frontend trigger a mutation, which records the user’s intent (even if you don’t need to persist much data), and then have that mutation schedule the action to run in the background. This keeps your API endpoints private, allows you to enforce invariants (like deduplication or authorization), and fits Convex’s intended workflow. Even if you don’t need to save much data, you can insert a minimal record (like a log or a task) and then schedule the action. This pattern is more scalable and secure, and it allows you to track or audit background jobs if needed in the future.

  • Simplicity vs. Correctness: While it might seem simpler to just call an action from the frontend, this approach sacrifices security and maintainability. The extra step of a mutation is intentional and helps keep your backend robust and private.

Example of the recommended pattern:

import { v } from "convex/values";
import { internal } from "./_generated/api";
import { internalAction, mutation } from "./_generated/server";
#

export const triggerApiCall = mutation({
args: { eventId: v.string() },
handler: async (ctx, { eventId }) => {
// Optionally insert a minimal record or just schedule the action
await ctx.scheduler.runAfter(0, internal.myFunctions.backgroundApiCall, { eventId });
},
});

export const backgroundApiCall = internalAction({
args: { eventId: v.string() },
handler: async (_, args) => {
// Call your third-party API here
},
});

Call the mutation from your frontend, not the action directly.

**Summary:**  
Even if you don’t need to save data, using a mutation to schedule an action is the recommended and secure way to trigger background API calls from your frontend in Convex. Directly calling actions from the client is possible, but discouraged and considered an anti-pattern for privacy and security reasons. [Convex docs: Calling actions from clients](<https://docs.convex.dev/functions/actions#calling-actions-from-clients>) and [Zen of Convex](<https://docs.convex.dev/understanding/zen>).
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
#

@wide stag Kapa.ai is still learning and improving, please let me know how I did by reacting below.

wide stag
#

@wise granite

The docs mention “in most cases” analyze my specific case and advice me. I will have an event listener for new purchases and when new purchase appears, I want to call my action to send a simple notification. I won’t let the user call my action directly, it’s in the front end, yes but no visible element, simply a trigger for an event for my payments sdk

wise graniteBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

In your scenario—where a frontend event listener for new purchases triggers a notification via a Convex action, and there’s no visible UI element for the user to interact with—the core concern remains: calling an action directly from the frontend is still considered an anti-pattern in Convex, even if the trigger is not user-facing.

Here’s why, based on the official guidance:

  • Actions called from the client are public: Any code running in the browser can be inspected and invoked by a determined user or malicious actor. Even if there’s no visible UI, the action endpoint is still exposed to the public internet and can be called by anyone who knows its name and arguments. This is a fundamental property of how Convex exposes public functions, not just a matter of UI visibility Authentication Best Practices: Convex, Clerk and NextJs.

  • Recommended pattern: The Convex docs strongly recommend using a mutation to record the event (even minimally) and then scheduling the action from the mutation. This allows you to enforce invariants, audit calls, and—most importantly—keep your action private by making it internal. This pattern is recommended even for simple notification use cases, not just for complex workflows Calling actions from clients.

  • Why “in most cases”?: The docs say “in most cases” because there are rare situations where you might want to expose an action directly (e.g., if you truly don’t care about security, rate limiting, or abuse, and the action is idempotent and harmless). However, for anything related to payments or notifications, it’s best to follow the recommended pattern to avoid future issues.

#

Summary for your case:
Even though your action is triggered by a frontend event listener and not a visible UI, it is still exposed to the client and thus to the public. The recommended and secure approach is to have your frontend call a mutation, which then schedules the notification action. This keeps your backend secure and maintainable.

“In most cases calling an action directly from a client is an anti-pattern. Instead, have the client call a mutation which captures the user intent by writing into the database and then schedules an action.”
Convex docs: Calling actions from clients

Advice:
Use a mutation to trigger your notification action, even if you don’t need to persist much data. This is the best practice for security and future-proofing your app.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@wide stag Kapa.ai is still learning and improving, please let me know how I did by reacting below.

wide stag
#

@wise granite

But I mean, I simply want to send an API call to my own notification system (ntfy.sh)
It is harmless and if people abuse it I will simple get a lot
Of notifications, nothing risky it’s just for my personal use

wise graniteBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

Given your specific use case—sending a harmless notification to your own ntfy.sh system for personal use, where abuse would only result in extra notifications to yourself—the Convex documentation does acknowledge that calling actions directly from the client is technically possible. The docs say that in "most cases" it's an anti-pattern, but they leave room for exceptions where the risk is low and the action is idempotent or harmless.

In your scenario, since:

  • The action is not exposed via a visible UI element,
  • The notification system is for your personal use,
  • Abuse would not have significant consequences,

it is reasonable to call the action directly from your frontend, especially if you value simplicity and are aware of the trade-offs. The main risk is that the action endpoint is public and could be triggered by anyone with access to your frontend code, but in your case, this only results in extra notifications to yourself, which you find acceptable.

Summary:
While Convex recommends using a mutation to schedule actions for security and best practices, your use case is a valid exception where calling the action directly from the frontend is acceptable due to the low risk and personal context. Just be aware that the action is public and could be triggered by others if they inspect your frontend code, but for your described scenario, this is not a significant concern Calling actions from clients.

If your requirements change in the future (e.g., you want to prevent abuse or add more sensitive logic), consider switching to the recommended mutation + action pattern.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: