fix: support all where operators in list filter endpoints

This commit is contained in:
Taesu
2026-02-08 21:39:10 +09:00
parent 34fe5f98c4
commit 59e3f20760
7 changed files with 75 additions and 23 deletions

View File

@@ -153,11 +153,11 @@ type listUsers = {
/**
* The value to filter by.
*/
filterValue?: string | number | boolean = "hello@example.com"
filterValue?: string | number | boolean | string[] = "hello@example.com"
/**
* The operator to use for the filter.
* The operator to use for the filter.
*/
filterOperator?: "eq" | "ne" | "lt" | "lte" | "gt" | "gte" = "eq"
filterOperator?: "eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "in" | "not_in" | "contains" | "starts_with" | "ends_with" = "eq"
}
```
</APIMethod>

View File

@@ -1090,11 +1090,11 @@ type listMembers = {
/**
* The operator to filter by.
*/
filterOperator?: "eq" | "ne" | "gt" | "gte" | "lt" | "lte" | "in" | "nin" | "contains" = "eq"
filterOperator?: "eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "in" | "not_in" | "contains" | "starts_with" | "ends_with" = "eq"
/**
* The value to filter by.
*/
filterValue?: string = "value"
filterValue?: string | number | boolean | string[] = "value"
}
```
</APIMethod>

View File

@@ -4,6 +4,7 @@ import {
} from "@better-auth/core/api";
import type { Session } from "@better-auth/core/db";
import type { Where } from "@better-auth/core/db/adapter";
import { whereOperators } from "@better-auth/core/db/adapter";
import { APIError, BASE_ERROR_CODES } from "@better-auth/core/error";
import * as z from "zod";
import { getSessionFromCtx } from "../../api";
@@ -574,9 +575,10 @@ const listUsersQuerySchema = z.object({
})
.or(z.number())
.or(z.boolean())
.or(z.array(z.string()))
.optional(),
filterOperator: z
.enum(["eq", "ne", "lt", "lte", "gt", "gte", "contains"])
.enum(whereOperators)
.meta({
description: "The operator to use for the filter",
})

View File

@@ -1,5 +1,6 @@
import type { AuthContext, GenericEndpointContext } from "@better-auth/core";
import { getCurrentAdapter } from "@better-auth/core/context";
import type { WhereOperator } from "@better-auth/core/db/adapter";
import { BetterAuthError } from "@better-auth/core/error";
import { filterOutputFields } from "@better-auth/core/utils/db";
import { parseJSON } from "../../client/parser";
@@ -134,7 +135,7 @@ export const getOrgAdapter = <O extends OrganizationOptions>(
filter?:
| {
field: string;
operator?: "eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "contains";
operator?: WhereOperator;
value: any;
}
| undefined;

View File

@@ -144,6 +144,51 @@ describe("listMembers", async () => {
expect(members.data?.total).toBe(10);
});
it("should filter the members with 'in' operator", async () => {
const members = await client.organization.listMembers({
fetchOptions: {
headers,
},
query: {
filterField: "role",
filterOperator: "in",
filterValue: ["member", "owner"],
},
});
expect(members.data?.members.length).toBe(11);
expect(members.data?.total).toBe(11);
});
it("should filter the members with 'not_in' operator", async () => {
const members = await client.organization.listMembers({
fetchOptions: {
headers,
},
query: {
filterField: "role",
filterOperator: "not_in",
filterValue: ["owner"],
},
});
expect(members.data?.members.length).toBe(10);
expect(members.data?.total).toBe(10);
});
it("should filter the members with 'starts_with' operator", async () => {
const members = await client.organization.listMembers({
fetchOptions: {
headers,
},
query: {
filterField: "role",
filterOperator: "starts_with",
filterValue: "mem",
},
});
expect(members.data?.members.length).toBe(10);
expect(members.data?.total).toBe(10);
});
it("should sort the members", async () => {
const defaultMembers = await client.organization.listMembers({
fetchOptions: {

View File

@@ -1,5 +1,6 @@
import type { LiteralString } from "@better-auth/core";
import { createAuthEndpoint } from "@better-auth/core/api";
import { whereOperators } from "@better-auth/core/db/adapter";
import { APIError, BASE_ERROR_CODES } from "@better-auth/core/error";
import * as z from "zod";
import { getSessionFromCtx, sessionMiddleware } from "../../../api";
@@ -879,9 +880,10 @@ export const listMembers = <O extends OrganizationOptions>(options: O) =>
})
.or(z.number())
.or(z.boolean())
.or(z.array(z.string()))
.optional(),
filterOperator: z
.enum(["eq", "ne", "lt", "lte", "gt", "gte", "contains"])
.enum(whereOperators)
.meta({
description: "The operator to use for the filter",
})

View File

@@ -301,25 +301,27 @@ export interface DBAdapterFactoryConfig<
disableTransformJoin?: boolean | undefined;
}
export const whereOperators = [
"eq",
"ne",
"lt",
"lte",
"gt",
"gte",
"in",
"not_in",
"contains",
"starts_with",
"ends_with",
] as const;
export type WhereOperator = (typeof whereOperators)[number];
export type Where = {
/**
* @default eq
*/
operator?:
| (
| "eq"
| "ne"
| "lt"
| "lte"
| "gt"
| "gte"
| "in"
| "not_in"
| "contains"
| "starts_with"
| "ends_with"
)
| undefined;
operator?: WhereOperator | undefined;
value: string | number | boolean | string[] | number[] | Date | null;
field: string;
/**