From a60055782d69981d0b62fe8fcb2867e0cc9685f6 Mon Sep 17 00:00:00 2001 From: 0x62 Date: Mon, 18 Mar 2024 13:58:10 +0000 Subject: [PATCH 1/2] feat: add filter support to listUsers --- src/GoTrueAdminApi.ts | 15 +++++++++++---- test/GoTrueApi.test.ts | 22 ++++++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/GoTrueAdminApi.ts b/src/GoTrueAdminApi.ts index 8f4ee2ba..a386cd4c 100644 --- a/src/GoTrueAdminApi.ts +++ b/src/GoTrueAdminApi.ts @@ -169,10 +169,11 @@ export default class GoTrueAdminApi { * Get a list of users. * * This function should only be called on a server. Never expose your `service_role` key in the browser. - * @param params An object which supports `page` and `perPage` as numbers, to alter the paginated results. + * @param params An object which supports `page` and `perPage` as numbers, to alter the paginated results, + * and `filter` string to search for users by email address. */ async listUsers( - params?: PageParams + params?: PageParams & { filter?: string } ): Promise< | { data: { users: User[]; aud: string } & Pagination; error: null } | { data: { users: [] }; error: AuthError } @@ -185,6 +186,7 @@ export default class GoTrueAdminApi { query: { page: params?.page?.toString() ?? '', per_page: params?.perPage?.toString() ?? '', + ...(params?.filter ? { filter: params.filter } : {}), }, xform: _noResolveJsonResponse, }) @@ -195,8 +197,13 @@ export default class GoTrueAdminApi { const links = response.headers.get('link')?.split(',') ?? [] if (links.length > 0) { links.forEach((link: string) => { - const page = parseInt(link.split(';')[0].split('=')[1].substring(0, 1)) - const rel = JSON.parse(link.split(';')[1].split('=')[1]) + const [urlPart, relPart] = link.split(';').map(part => part.trim()); + const url = urlPart.slice(1, -1); // Remove the leading "" + + const searchParams = new URLSearchParams(url.split('?')[1]); + const page = parseInt(searchParams.get('page') || '1', 10); + const rel = relPart.split('=')[1].replace(/"/g, ''); + pagination[`${rel}Page`] = page }) diff --git a/test/GoTrueApi.test.ts b/test/GoTrueApi.test.ts index e1afc076..e007a626 100644 --- a/test/GoTrueApi.test.ts +++ b/test/GoTrueApi.test.ts @@ -99,6 +99,28 @@ describe('GoTrueAdminApi', () => { expect(emails.length).toBeGreaterThan(0) expect(emails).toContain(email) }) + + test('listUsers() should support filter property', async () => { + const { email } = mockUserCredentials() + const { error: createError, data: createdUser } = await createNewUserWithEmail({ email }) + expect(createError).toBeNull() + expect(createdUser.user).not.toBeUndefined() + + const { error: listUserError, data: userList } = await serviceRoleApiClient.listUsers({ + filter: email, + }) + expect(listUserError).toBeNull() + expect(userList).toHaveProperty('users') + expect(userList).toHaveProperty('aud') + + const emails = + userList.users?.map((user: User) => { + return user.email + }) || [] + + expect(emails.length).toEqual(1) + expect(emails).toContain(email) + }) test('getUser() fetches a user by their access_token', async () => { const { email, password } = mockUserCredentials() From b36a297b312dbdde9bb1b0ee801149350e7dea77 Mon Sep 17 00:00:00 2001 From: Ben Date: Thu, 2 Oct 2025 11:41:20 +0100 Subject: [PATCH 2/2] Validation & JSDoc warning --- src/GoTrueAdminApi.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/GoTrueAdminApi.ts b/src/GoTrueAdminApi.ts index bb8198e7..df7aa196 100644 --- a/src/GoTrueAdminApi.ts +++ b/src/GoTrueAdminApi.ts @@ -179,6 +179,9 @@ export default class GoTrueAdminApi { * This function should only be called on a server. Never expose your `service_role` key in the browser. * @param params An object which supports `page` and `perPage` as numbers, to alter the paginated results, * and `filter` string to search for users by email address. + * + * @warning The filter parameter is provided for convenience but may have performance implications on large databases. + * Consider using pagination without filters for projects with many users. */ async listUsers( params?: PageParams & { filter?: string } @@ -187,6 +190,10 @@ export default class GoTrueAdminApi { | { data: { users: [] }; error: AuthError } > { try { + if (params?.filter && params.filter.trim().length < 3) { + throw new AuthApiError('Filter must be at least 3 characters', 400); + } + const pagination: Pagination = { nextPage: null, lastPage: 0, total: 0 } const response = await _request(this.fetch, 'GET', `${this.url}/admin/users`, { headers: this.headers,