#Fix this get current user

14 messages · Page 1 of 1 (latest)

wanton wren
#

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

const existingUser = await ctx.db.get(userId);
if (!existingUser) {
  return null;
}

if (!existingUser.role) {
  throw new ConvexError("User does not have a role");
}

return {
  ...existingUser,
  role: VALID_ROLES.STUDENT,
};

},
});

export const getCurrentUserOrThrow = query({
args: {},
handler: async (ctx) => {
const user = await ctx.runQuery(api.users.getCurrentUser);
if (!user) {
throw new ConvexError("User does not exists");
}

return user;

},
});

twilit dockBOT
#

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

#

Based on the knowledge sources provided, I can suggest some improvements to your getCurrentUser and getCurrentUserOrThrow functions. Here's an updated version that incorporates best practices from Convex Auth:

import { query } from "./_generated/server";
import { getAuthUserId } from "@convex-dev/auth/server";
import { ConvexError } from "convex/values";

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

    const existingUser = await ctx.db.get(userId);
    if (!existingUser) {
      return null;
    }

    if (!existingUser.role) {
      throw new ConvexError("User does not have a role");
    }

    return {
      ...existingUser,
      role: VALID_ROLES.STUDENT,
    };
  },
});

export const getCurrentUserOrThrow = query({
  args: {},
  handler: async (ctx) => {
    const userId = await getAuthUserId(ctx);
    if (userId === null) {
      throw new ConvexError("User is not authenticated");
    }

    const user = await ctx.db.get(userId);
    if (!user) {
      throw new ConvexError("User does not exist");
    }

    if (!user.role) {
      throw new ConvexError("User does not have a role");
    }

    return {
      ...user,
      role: VALID_ROLES.STUDENT,
    };
  },
});

Key changes and explanations:

  1. We're using getAuthUserId from @convex-dev/auth/server to get the current user's ID, as recommended in the Convex Auth documentation (https://labs.convex.dev/auth/api_reference/server#getauthuserId).

  2. In getCurrentUserOrThrow, we're not using ctx.runQuery to call getCurrentUser. Instead, we're directly implementing the logic to get the user. This is more efficient and avoids unnecessary query nesting.

#
  1. We're consistently using ConvexError for throwing errors, which is a good practice in Convex functions.

  2. The logic for checking the user's role and returning the user object is consistent between both functions.

  3. In getCurrentUserOrThrow, we're throwing an error if the user is not authenticated, which is more appropriate for a function that's expected to always return a user.

Remember to import necessary dependencies and ensure that VALID_ROLES is defined in your code.

This implementation should work well with Convex Auth and provide a consistent way to get the current user's information in your Convex functions. You can use these functions in your queries and mutations as needed.

Convex Auth API Reference (https://labs.convex.dev/auth/api_reference/server)
Convex Auth in Functions (https://docs.convex.dev/auth/functions-auth)

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

#

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

wanton wren
#

How can I get the current user in a reusable way?

import { getAuthUserId } from "@convex-dev/auth/server";
import { query } from "./_generated/server";
import { v } from "convex/values";
import { mutation } from "./_generated/server";
import { BookingStatus, BookingType } from "./lib/bookings";
import { api } from "./_generated/api";

export const create = mutation({
args: {
tutor: v.id("users"),
service: v.id("tutors_services"),
type: v.union(
v.literal(BookingType.FREE_MEETING),
v.literal(BookingType.LESSON)
),
startTime: v.number(),
endTime: v.number(),
},
handler: async (ctx, args) => {
const user = await ctx.runQuery(api.users.getCurrentUserOrThrow);

const booking = await ctx.db.insert("bookings", {
  student: user._id,
  tutor: args.tutor,
  service: args.service,
  type: args.type,
  status: BookingStatus.AWAITING_TUTOR_CONFIRMATION,
  startTime: args.startTime,
  endTime: args.endTime,
  createdAt: Date.now(),
  updatedAt: Date.now(),
});

return booking;

},
});

@twilit dock

twilit dockBOT
#

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

#

To get the current user in a reusable way in Convex, you can create a helper function that can be used across your queries and mutations. Here's how you can modify your code to achieve this:

  1. First, create a helper function in a separate file, let's call it userHelpers.ts:
import { QueryCtx } from "./_generated/server";
import { getAuthUserId } from "@convex-dev/auth/server";
import { ConvexError } from "convex/values";

export async function getCurrentUserOrThrow(ctx: QueryCtx) {
  const userId = await getAuthUserId(ctx);
  if (userId === null) {
    throw new ConvexError("Not authenticated");
  }
  const user = await ctx.db.get(userId);
  if (!user) {
    throw new ConvexError("User not found");
  }
  return user;
}
  1. Now, you can use this helper function in your mutation:
import { mutation } from "./_generated/server";
import { v } from "convex/values";
import { BookingStatus, BookingType } from "./lib/bookings";
import { getCurrentUserOrThrow } from "./userHelpers";

export const create = mutation({
  args: {
    tutor: v.id("users"),
    service: v.id("tutors_services"),
    type: v.union(
      v.literal(BookingType.FREE_MEETING),
      v.literal(BookingType.LESSON)
    ),
    startTime: v.number(),
    endTime: v.number(),
  },
  handler: async (ctx, args) => {
    const user = await getCurrentUserOrThrow(ctx);

    const booking = await ctx.db.insert("bookings", {
      student: user._id,
      tutor: args.tutor,
      service: args.service,
      type: args.type,
      status: BookingStatus.AWAITING_TUTOR_CONFIRMATION,
      startTime: args.startTime,
      endTime: args.endTime,
      createdAt: Date.now(),
      updatedAt: Date.now(),
    });

    return booking;
  },
});

This approach has several advantages:

#
  1. It's reusable across all your Convex functions (queries, mutations, and actions).
  2. It encapsulates the logic for getting the current user, making your code more maintainable.
  3. It uses getAuthUserId from Convex Auth, which is the recommended way to get the current user's ID as per the Convex Auth documentation (https://labs.convex.dev/auth/api_reference/server#getauthuserId).
  4. It throws appropriate errors if the user is not authenticated or not found in the database.

By using this helper function, you avoid the need to run a separate query to get the current user, which is more efficient. You can now use getCurrentUserOrThrow in any of your Convex functions where you need to access the current user's information.

Convex Auth API Reference (https://labs.convex.dev/auth/api_reference/server)
Convex Best Practices (https://docs.convex.dev/production/best-practices/#functions)

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

#

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

wanton wren
#

I want to fix this return type of role / name be a string | undefined, it can only be a string
import { getAuthUserId } from "@convex-dev/auth/server";
import { ConvexError } from "convex/values";
import { QueryCtx } from "../_generated/server";

export async function getCurrentUserOrThrow(ctx: QueryCtx) {
const userId = await getAuthUserId(ctx);
if (userId === null) {
throw new ConvexError("Not authenticated");
}
const user = await ctx.db.get(userId);
if (!user) {
throw new ConvexError("User not found");
}

if (user.role === undefined || user.name === undefined) {
throw new ConvexError("User needs a name and a role");
}

return {
...user,
};
}

@twilit dock

twilit dockBOT
#

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

#

To fix the return type of role and name to be strictly string and not string | undefined, you can use TypeScript's type assertion or a type guard. Here's how you can modify your getCurrentUserOrThrow function to achieve this:

import { getAuthUserId } from "@convex-dev/auth/server";
import { ConvexError } from "convex/values";
import { QueryCtx } from "../_generated/server";
import { Doc } from "./_generated/dataModel";

export async function getCurrentUserOrThrow(ctx: QueryCtx): Promise<Doc<"users"> & { role: string; name: string }> {
  const userId = await getAuthUserId(ctx);
  if (userId === null) {
    throw new ConvexError("Not authenticated");
  }
  const user = await ctx.db.get(userId);
  if (!user) {
    throw new ConvexError("User not found");
  }

  if (typeof user.role !== 'string' || typeof user.name !== 'string') {
    throw new ConvexError("User needs a name and a role");
  }

  return {
    ...user,
    role: user.role,
    name: user.name
  };
}

Let's break down the changes:

  1. We've added a return type to the function: Promise<Doc<"users"> & { role: string; name: string }>. This tells TypeScript that the function will return a promise that resolves to a user document with role and name properties that are definitely strings.

  2. We've changed the type check to use typeof user.role !== 'string' and typeof user.name !== 'string'. This is a type guard that ensures these properties are strings.

  3. In the return statement, we're explicitly including role and name in the returned object. This, combined with the return type annotation, tells TypeScript that these properties are definitely strings.

#

This approach ensures that the role and name properties of the returned user object are always strings, and TypeScript will recognize them as such. If either role or name is undefined or not a string, the function will throw an error.

Remember to import the Doc type from your generated data model file to ensure type safety for the user document.

Convex TypeScript Best Practices (https://docs.convex.dev/production/best-practices/typescript)
Convex Data Model Types (https://docs.convex.dev/database/schemas#typescript-types)

Note: Make sure your users table schema in Convex defines role and name as required string fields to maintain consistency between your database schema and this TypeScript implementation.

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