Convex internal vs public: securing your functions and not exposing everything

1 min readApril 29, 2026#convex#sécurité#backend#typescript

Convex: internal vs public — don't expose what doesn't need exposing

By default, any Convex function exported with export const is reachable from the frontend.

export const confirmPayment = mutation({ ... })
// → anyone can call api.payments.confirmPayment from the browser

For a payment confirmation function, that's a problem. You don't want a user calling this directly from their browser console.


The distinction is simple:

// Reachable from the frontend — for real user interactions
export const getMyOrders = query({ ... });
 
// Reachable only from the Convex backend — never from the client
export const confirmPayment = internalMutation({ ... });
export const sendNotification = internalAction({ ... });

The rule: anything that touches financial data, critical statuses, or user access → internal.

Calling it from the backend:

await ctx.runMutation(internal.payments.confirmPayment, { orderId });
// internal. instead of api.

api. → frontend can call it. internal. → backend only.

When in doubt: would you be comfortable with a curious user calling it from their console? If not — it's internal.