#Run query within custom ctx

4 messages · Page 1 of 1 (latest)

icy valley
#

Here is my query:

import { getAuthUserId } from "@convex-dev/auth/server"

import { query } from "@cvx/_generated/server"

export const getCurrentUser = query({
  args: {},
  handler: async (ctx) => {
    const userId = await getAuthUserId(ctx)
    if (!userId) return null

    const user = await ctx.db.get(userId)
    if (!user) return null

    return user
  },
})

Then I have a wrapper function like this:

import { getAuthUserId } from "@convex-dev/auth/server"
import { customCtx } from "convex-helpers/server/customFunctions"
import { zCustomMutation, zCustomQuery } from "convex-helpers/server/zod"
import { ConvexError } from "convex/values"

import { mutation, query } from "@cvx/_generated/server"
import { getCurrentUser } from "@cvx/users/queries"

export const authenticatedQuery = zCustomQuery(
  query,
  customCtx(async (ctx) => {
    const user = await getCurrentUser(ctx)
    if (!user) throw new ConvexError("Unauthorized")

    return { user }
  })
)

Here is the typescript error:
This expression is not callable.
Type 'RegisteredQuery<"public", {}, Promise<{ _id: Id<"users">; _creationTime: number; image?: string | undefined; name: string; email: string; isAuthorized: boolean; } | null>>' has no call signatures

How can I call the getCurrentUser inside the customCtx?

random sierraBOT
#

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

#

The error you're encountering is because you're trying to directly call the getCurrentUser function, which is a registered Convex query, rather than using the appropriate method to run a query within another Convex function.

When working within a custom context function, you should use ctx.runQuery to execute another query instead of calling it directly. This is because Convex functions are not meant to be called directly - they need to be executed through the Convex runtime.

Here's how you can fix your code:

import { getAuthUserId } from "@convex-dev/auth/server"
import { customCtx } from "convex-helpers/server/customFunctions"
import { zCustomMutation, zCustomQuery } from "convex-helpers/server/zod"
import { ConvexError } from "convex/values"

import { mutation, query } from "@cvx/_generated/server"
import { api } from "@cvx/_generated/api"

export const authenticatedQuery = zCustomQuery(
  query,
  customCtx(async (ctx) => {
    const user = await ctx.runQuery(api.users.queries.getCurrentUser)
    if (!user) throw new ConvexError("Unauthorized")

    return { user }
  })
)

The key change is using ctx.runQuery with a reference to your query from the generated API. This is the recommended approach when you need to run a query from within another Convex function.

Note that while you could also refactor your code to use helper functions instead of calling queries directly, which is generally considered a best practice in Convex. As mentioned in the Convex documentation, using helper functions can make your code more maintainable and efficient.

For example, you could extract the logic from getCurrentUser into a helper function that both your query and custom context can use:

#
// In a helper file like model/users.ts
import { QueryCtx } from '../_generated/server';
import { getAuthUserId } from "@convex-dev/auth/server";

export async function getCurrentUser(ctx: QueryCtx) {
  const userId = await getAuthUserId(ctx);
  if (!userId) return null;

  const user = await ctx.db.get(userId);
  if (!user) return null;

  return user;
}

// Then in your queries file
import { getCurrentUser } from "./model/users";

export const getCurrentUserQuery = query({
  args: {},
  handler: async (ctx) => {
    return await getCurrentUser(ctx);
  },
});

// And in your custom context
import { getCurrentUser } from "./model/users";

export const authenticatedQuery = zCustomQuery(
  query,
  customCtx(async (ctx) => {
    const user = await getCurrentUser(ctx);
    if (!user) throw new ConvexError("Unauthorized");

    return { user };
  })
)

This approach is recommended by Convex as it avoids the overhead of running argument and return value validation that comes with using ctx.runQuery.

GitHub: Stack Convex - Authentication Best Practices

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