[PR #6352] [CLOSED] Fix/admin plugin get user additional fields #23513

Closed
opened 2026-04-15 21:46:56 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/better-auth/better-auth/pull/6352
Author: @Ridhim-RR
Created: 11/27/2025
Status: Closed

Base: canaryHead: fix/admin-plugin-getUser-additionalFields


📝 Commits (6)

  • 28fee85 fix(adminPlugin):getUser additional fields
  • 4eed70d fic(admin): extending the User object
  • bf9f313 chore(lint): linting issue resolved
  • b8e363b fix(adminPlugin):fix updateUser
  • 475e177 chore: chnge the type from InferUser to User & Record<string, any>
  • a5e5343 Merge branch 'canary' into fix/admin-plugin-getUser-additionalFields

📊 Changes

4 files changed (+47 additions, -5 deletions)

View changed files

📝 packages/better-auth/src/db/internal-adapter.ts (+4 -2)
📝 packages/better-auth/src/plugins/admin/admin.test.ts (+39 -0)
📝 packages/better-auth/src/plugins/admin/admin.ts (+3 -2)
📝 packages/core/src/types/context.ts (+1 -1)

📄 Description

Closes #6318

Problem

The getUser endpoint in the admin plugin was not returning custom/additional user fields defined in user.additionalFields configuration. While the fields existed in the database, they were not included in the API response.

Example:

// Config
user: {
additionalFields: {
bio: { type: "string", required: false }
}
}


// API call
const user = await auth.api.admin.getUser({ query: { id: userId } });
// ❌ user.bio is undefined, even though it exists in the database

Root Cause

The findUserById method in the internal adapter was constrained to return only the base User type:

// Before
findUserById: async (userId: string) => {
const user = await adapter.findOne<User>({ ... }); // ❌ Only base User type
return user;
}

This type constraint caused the adapter to potentially filter out fields that weren't part of the base User schema, even though parseUserOutput was designed to handle additional fields.

Solution

Updated findUserById to return User & Record<string, any> instead of just User, allowing all fields from the database to be included in the response.

Changes Made

1. Updated Type Definition

File: packages/core/src/types/context.ts

// Before
findUserById(userId: string): Promise<User | null>;


// After
findUserById(userId: string): Promise<User & Record<string, any> | null>;

2. Updated Implementation

File: packages/better-auth/src/db/internal-adapter.ts

// Before
findUserById: async (userId: string) => {
const user = await adapter.findOne<User>({ ... });
return user;
}


// After
findUserById: async (userId: string) => {
const user = await adapter.findOne<User & Record<string, any>>({ ... });
return user;
}

How It Works

  1. Database Query: findOne<User & Record<string, any>> tells the adapter to return all fields from the database, including additional fields like bio.

  2. Type Safety: User & Record<string, any> preserves the base User type while allowing additional fields, maintaining type safety for known fields.

  3. Field Processing: parseUserOutput receives the complete user object (including bio) and processes it according to the schema configuration, respecting returned: false flags and applying output transformations.

  4. Response: The endpoint returns the full user object with all additional fields included.

Testing

Added test case to verify additional fields are returned:

it("should return additional fields when getting user", async () => {
// Update user with bio field
await client.admin.updateUser({
userId: testNonAdminUser.id,
data: { bio: "This is my bio" },
}, { headers: adminHeaders });


// Get user
const res = await client.admin.getUser({
query: { id: testNonAdminUser.id },
}, { headers: adminHeaders });


// Assert bio is present
expect(res.data?.bio).toBe("This is my bio");
});

Summary by cubic

Fixes admin.getUser so it returns custom fields defined in user.additionalFields (e.g., bio), ensuring the full user object is included in the response.

  • Bug Fixes
    • Widened InternalAdapter.findUserById to return User & Record<string, any> so extra fields aren’t dropped.
    • Updated admin.getUser to parse and return User & Record<string, any>.
    • Fixed admin.updateUser to handle additional fields by widening the update type.
    • Added a test to confirm additional fields like bio are returned.

Written for commit a5e5343d99. Summary will update automatically on new commits.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/better-auth/better-auth/pull/6352 **Author:** [@Ridhim-RR](https://github.com/Ridhim-RR) **Created:** 11/27/2025 **Status:** ❌ Closed **Base:** `canary` ← **Head:** `fix/admin-plugin-getUser-additionalFields` --- ### 📝 Commits (6) - [`28fee85`](https://github.com/better-auth/better-auth/commit/28fee85e55ebd33e48b6ddfa6e28166e065a9b89) fix(adminPlugin):getUser additional fields - [`4eed70d`](https://github.com/better-auth/better-auth/commit/4eed70d9b519e4cf9b118af624e8e2b31f7ac010) fic(admin): extending the User object - [`bf9f313`](https://github.com/better-auth/better-auth/commit/bf9f313d86e5437ed5c7713b33b20bee5eb2592f) chore(lint): linting issue resolved - [`b8e363b`](https://github.com/better-auth/better-auth/commit/b8e363bee3e21a434ea4a1d51033febf862e9bbe) fix(adminPlugin):fix updateUser - [`475e177`](https://github.com/better-auth/better-auth/commit/475e1776f26946c71b179c7346fd67184e649d24) chore: chnge the type from InferUser to User & Record<string, any> - [`a5e5343`](https://github.com/better-auth/better-auth/commit/a5e5343d99b81c2fc83f2f3f30db643ea52ab2b1) Merge branch 'canary' into fix/admin-plugin-getUser-additionalFields ### 📊 Changes **4 files changed** (+47 additions, -5 deletions) <details> <summary>View changed files</summary> 📝 `packages/better-auth/src/db/internal-adapter.ts` (+4 -2) 📝 `packages/better-auth/src/plugins/admin/admin.test.ts` (+39 -0) 📝 `packages/better-auth/src/plugins/admin/admin.ts` (+3 -2) 📝 `packages/core/src/types/context.ts` (+1 -1) </details> ### 📄 Description Closes #6318 ## Problem The `getUser` endpoint in the admin plugin was not returning custom/additional user fields defined in `user.additionalFields` configuration. While the fields existed in the database, they were not included in the API response. **Example:** ```typescript // Config user: { additionalFields: { bio: { type: "string", required: false } } } // API call const user = await auth.api.admin.getUser({ query: { id: userId } }); // ❌ user.bio is undefined, even though it exists in the database ``` ## Root Cause The `findUserById` method in the internal adapter was constrained to return only the base `User` type: ```typescript // Before findUserById: async (userId: string) => { const user = await adapter.findOne<User>({ ... }); // ❌ Only base User type return user; } ``` This type constraint caused the adapter to potentially filter out fields that weren't part of the base `User` schema, even though `parseUserOutput` was designed to handle additional fields. ## Solution Updated `findUserById` to return `User & Record<string, any>` instead of just `User`, allowing all fields from the database to be included in the response. ## Changes Made ### 1. Updated Type Definition **File:** `packages/core/src/types/context.ts` ```typescript // Before findUserById(userId: string): Promise<User | null>; // After findUserById(userId: string): Promise<User & Record<string, any> | null>; ``` ### 2. Updated Implementation **File:** `packages/better-auth/src/db/internal-adapter.ts` ```typescript // Before findUserById: async (userId: string) => { const user = await adapter.findOne<User>({ ... }); return user; } // After findUserById: async (userId: string) => { const user = await adapter.findOne<User & Record<string, any>>({ ... }); return user; } ``` ## How It Works 1. **Database Query**: `findOne<User & Record<string, any>>` tells the adapter to return all fields from the database, including additional fields like `bio`. 2. **Type Safety**: `User & Record<string, any>` preserves the base `User` type while allowing additional fields, maintaining type safety for known fields. 3. **Field Processing**: `parseUserOutput` receives the complete user object (including `bio`) and processes it according to the schema configuration, respecting `returned: false` flags and applying output transformations. 4. **Response**: The endpoint returns the full user object with all additional fields included. ## Testing Added test case to verify additional fields are returned: ```typescript it("should return additional fields when getting user", async () => { // Update user with bio field await client.admin.updateUser({ userId: testNonAdminUser.id, data: { bio: "This is my bio" }, }, { headers: adminHeaders }); // Get user const res = await client.admin.getUser({ query: { id: testNonAdminUser.id }, }, { headers: adminHeaders }); // Assert bio is present expect(res.data?.bio).toBe("This is my bio"); }); ``` <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Fixes admin.getUser so it returns custom fields defined in user.additionalFields (e.g., bio), ensuring the full user object is included in the response. - **Bug Fixes** - Widened InternalAdapter.findUserById to return User & Record<string, any> so extra fields aren’t dropped. - Updated admin.getUser to parse and return User & Record<string, any>. - Fixed admin.updateUser to handle additional fields by widening the update type. - Added a test to confirm additional fields like bio are returned. <sup>Written for commit a5e5343d99b81c2fc83f2f3f30db643ea52ab2b1. Summary will update automatically on new commits.</sup> <!-- End of auto-generated description by cubic. --> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-04-15 21:46:56 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#23513