#Calling convex functions from sveltekit server-side without exposing function

20 messages · Page 1 of 1 (latest)

high yacht
#

How can I call convex functions from sveltekit server-side without making the function public to the client?

mystic ridgeBOT
#

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

#

To call Convex functions from SvelteKit server-side code without exposing those functions to the client, you should use Convex "internal functions." Internal functions (defined with internalQuery, internalMutation, or internalAction) can only be called from other Convex functions, actions, or HTTP actions—not directly from the client. This is the recommended way to keep sensitive logic server-only and not accessible from the browser or public API.

How to do this in SvelteKit:

  1. Define Internal Functions in Convex

    In your Convex backend (e.g., src/convex/yourFile.ts):

    import { internalQuery } from "./_generated/server";
    import { v } from "convex/values";
    
    export const getSecretData = internalQuery({
      args: { id: v.id("secrets") },
      handler: async (ctx, args) => {
        return await ctx.db.get(args.id);
      },
    });
    
  2. Expose a Public Action or HTTP Action as a Gateway (Optional)

    If you need to call the internal function from outside Convex (e.g., from your SvelteKit server), you can create a public action or HTTP action that acts as a gateway. This action can perform authentication/authorization and then call the internal function using ctx.runQuery or ctx.runMutation:

    import { action } from "./_generated/server";
    import { internal } from "./_generated/api";
    import { v } from "convex/values";
    
    export const getSecretDataGateway = action({
      args: { id: v.id("secrets") },
      handler: async (ctx, args) => {
        // Add your own authentication/authorization logic here
        return await ctx.runQuery(internal.yourFile.getSecretData, { id: args.id });
      },
    });
    

    Internal Functions

  3. Call the Convex Action from SvelteKit Server Code

#

Use a Convex HTTP client in your SvelteKit server code to call the public action (not the internal function directly):

import { api } from "$convex/_generated/api";
import { createConvexHttpClient } from "@mmailaender/convex-better-auth-svelte/sveltekit";

export const load = async ({ locals }) => {
  const client = createConvexHttpClient({ token: locals.token });
  const secretData = await client.action(api.yourFile.getSecretDataGateway, { id: "your-id" });
  return { secretData };
};

SvelteKit + Convex Better Auth Example

Summary:

  • Define sensitive logic as internal functions in Convex.
  • If you need to call them from SvelteKit, create a public action or HTTP action as a secure gateway.
  • Call that gateway from your SvelteKit server code using a Convex HTTP client.
  • Never expose the internal function directly to the client.

This approach ensures your sensitive Convex logic is never directly accessible from the client, maintaining server-only access and security.

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

#

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

high yacht
#

@mystic ridge But having an http route is still public and a frontend client can still call this route and thus access the internal function

mystic ridgeBOT
#

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

#

You're correct: if you expose a Convex action or HTTP action as a gateway, that endpoint is technically public and can be called by any client, including the frontend, unless you add explicit access control.

To prevent unauthorized frontend access, you must implement strong authentication and authorization checks inside your public gateway function. This means:

  • Check that the request is coming from a trusted source (e.g., your server, not a browser client).
  • Use a shared secret, API key, or a signed JWT that only your backend knows and can send.
  • Validate this secret or token inside your Convex action or HTTP action before calling the internal function.

For example, you can require a secret in the request header and check it in your HTTP action:

const myHttpAction = (
  func: (ctx: ActionCtx, request: Request) => Promise<Response>,
): PublicHttpAction => {
  return httpAction(async (ctx, request) => {
    if (request.headers.get("Authorization") !== process.env.MY_SECRET) {
      return new Response("Unauthorized", { status: 401 });
    }
    return func(ctx, request);
  });
};

Discord: Custom function for HTTP action?

Or, if using a mutation/action, require a secret argument and check it matches an environment variable:

export const secureMutation = mutation({
  args: { secret: v.string(), ...otherArgs },
  handler: async (ctx, args) => {
    if (args.secret !== process.env.MY_SECRET) {
      throw new Error("Unauthorized");
    }
    // Call internal function here
  },
});

Discord: Private mutations and queries

#

Key point:
The public gateway is only secure if you enforce access control inside it. Without this, any client can call it and reach your internal logic. This is a common pattern for server-to-server communication, but you must not rely on obscurity—always check secrets or tokens on every request.

If you want to avoid passing secrets as arguments (to reduce risk of accidental logging), you can use headers or JWTs as described above. Some teams mint short-lived JWTs with custom claims for backend-only access, and verify those claims in the Convex function, which is more secure than a static shared secret if implemented correctly Discord: Restrict Convex mutation to external Vercel functions.

Summary:

  • The HTTP/action gateway is public by default.
  • You must add authentication/authorization checks to restrict access to your backend only.
  • Use secrets, API keys, or signed tokens to enforce this.
  • Never expose sensitive logic without these checks.

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

#

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

high yacht
#

@mystic ridge What's the difference between using http actions, actions, queries and mutations with server to server

mystic ridgeBOT
#

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

#

When calling Convex from another server (server-to-server), there are important differences between HTTP actions, actions, queries, and mutations:


1. HTTP Actions

  • Purpose: Expose custom HTTP endpoints directly from Convex, suitable for webhooks or custom APIs.
  • How to call: Make HTTP requests to <https://<your-deployment>.convex.site/<route>.>
  • Access: Publicly accessible unless you implement your own authentication/authorization.
  • Capabilities: Can call queries, mutations, and actions via ctx.runQuery, ctx.runMutation, and ctx.runAction.
  • Use case: Receiving webhooks, building custom HTTP APIs, or integrating with external services that require HTTP endpoints.
  • Note: You must handle argument parsing and security yourself. Request/response size is limited to 20MB. HTTP actions do not support argument validation by default, so you must validate and authenticate requests manually Defining HTTP actions Limits.

2. Actions

  • Purpose: Run arbitrary code (including network requests) in a Node.js environment, e.g., call external APIs.
  • How to call: Not directly callable over HTTP; must be invoked via the Convex client or from another Convex function.
  • Access: Can only be called from the Convex client or from other Convex functions (including HTTP actions).
  • Capabilities: Can call queries, mutations, and other actions via ctx.runQuery, ctx.runMutation, and ctx.runAction.
  • Use case: Server-side logic that needs to interact with external services, e.g., payment providers, AI APIs Actions.

#

3. Queries

  • Purpose: Read data from the Convex database in a deterministic, side-effect-free way.
  • How to call: Via the Convex client or HTTP API (/api/query endpoint).
  • Access: Public by default, but can be made internal to restrict access to other Convex functions only.
  • Capabilities: Only read data, no writes or side effects.
  • Use case: Fetching data for your application Server functions.

4. Mutations

  • Purpose: Write data to the Convex database transactionally.
  • How to call: Via the Convex client or HTTP API (/api/mutation endpoint).
  • Access: Public by default, but can be made internal to restrict access to other Convex functions only.
  • Capabilities: Read and write data, but no side effects (e.g., no network requests).
  • Use case: Creating, updating, or deleting data in your application Server functions.

Server-to-Server Considerations

  • Direct server-to-server calls: If you want to call Convex from another backend (e.g., from a SvelteKit or Node.js server), you can use the HTTP API to call public queries, mutations, or actions. Internal functions cannot be called directly from outside Convex; you must expose a public function as a gateway and implement your own authentication/authorization Discord: Call Internal Functions from Backend.
  • Security: Any public function (including HTTP actions) is accessible to anyone unless you add explicit access control. Use secrets, API keys, or JWTs to restrict access Discord: Private mutations and queries.

Summary Table:

#
Type HTTP Accessible Can Call from Server Needs Custom Auth Can Call Internal Functions Side Effects Allowed
HTTP Action Yes Yes Yes Yes Yes
Action No (client/Convex only) Yes (via client/Convex) N/A Yes Yes
Query Yes (HTTP API) Yes Yes (if public) Yes (if internal, only from Convex) No
Mutation Yes (HTTP API) Yes Yes (if public) Yes (if internal, only from Convex) No

If you need to call internal logic from another server, you must expose a public gateway (action or HTTP action) and secure it yourself. Internal functions are only callable from within Convex, not directly from external servers Internal Functions.

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

#

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

high yacht
#

@mystic ridge is ConvexHttpClient useful here?

mystic ridgeBOT
#

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

#

Yes, ConvexHttpClient is useful for server-to-server communication with Convex. It is designed for use in server-side environments (like Node.js, SvelteKit server code, or serverless functions) to call Convex queries, mutations, and actions over HTTP. You can use it to securely call your public Convex functions from your backend code.

Key points from the knowledge sources:

  • ConvexHttpClient is appropriate for server-side code and non-reactive webapps. It allows you to run queries and mutations over HTTP, and you can provide an authentication token if needed. It is stateful, so you should avoid sharing it between requests in a server environment Class: ConvexHttpClient.
  • You can use ConvexHttpClient in Node.js or any JavaScript runtime that implements fetch. Example usage:
    import { ConvexHttpClient } from "convex/browser";
    import { api } from "./convex/_generated/api";
    
    const client = new ConvexHttpClient(process.env["CONVEX_URL"]);
    const count = await client.query(api.counter.get);
    
    Client: JavaScript
  • If you need to authenticate requests (for example, to restrict access to server-only endpoints), you can set an auth token using the setAuth method on ConvexHttpClient ConvexHttpClient Methods.
#

Summary:
ConvexHttpClient is the recommended way to call Convex functions from your server-side code. However, remember that it can only call public functions directly. If you want to call internal functions, you must expose a secure public gateway function and implement your own authentication/authorization checks Call Internal Functions from Backend.

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