Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/purple-tools-punch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@refinedev/hasura": minor
---

Add support for Hasura JSONB/array operators
10 changes: 10 additions & 0 deletions documentation/docs/data/packages/hasura/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ const App = () => (
);
```

### Supported Operators

The `@refinedev/hasura` data provider correctly maps typical Crud operations to their equivalent Hasura `_` actions (e.g. `eq` -> `_eq`).

Additionally, native JSONB and Array operators are also supported, mapping easily right out-of-the-box:

- JSONB & Arrays: `_contains`, `_contained_in`, `_has_key`, `_has_keys_any`, `_has_keys_all`.

If an unsupported or custom operator string is used in a Refine query filter, the data provider automatically safely falls back—detecting the arbitrary operator and appending a `_` prefix (if not provided). It passes the arbitrary JSON payload directly downstream, neatly matching Hashura's conventions without throwing errors.

### Developer Experience

We suggest using `GraphQL Code Generator` to generate types for your queries and mutations. You can check out the [GraphQL Code Generator Documentation](https://the-guild.dev/graphql/codegen/docs/getting-started) to learn more about it.
Expand Down
35 changes: 31 additions & 4 deletions packages/hasura/src/utils/generateFilters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,25 @@ export type HasuraFilterCondition =
| "_similar"
| "_nsimilar"
| "_regex"
| "_iregex";
| "_iregex"
| "_contains"
| "_contained_in"
| "_has_key"
| "_has_keys_any"
| "_has_keys_all";

export type HasuraCrudOperators = CrudOperators | "not";
export type HasuraCrudOperators =
| CrudOperators
| "not"
| "contained_in"
| "has_key"
| "has_keys_any"
| "has_keys_all"
| "_contains"
| "_contained_in"
| "_has_key"
| "_has_keys_any"
| "_has_keys_all";

export type HasuraLogicalFilter = Omit<LogicalFilter, "operator"> & {
operator: Exclude<HasuraCrudOperators, "or" | "and" | "not">;
Expand Down Expand Up @@ -77,6 +93,15 @@ const hasuraFilters: Partial<
nstartswiths: "_nsimilar",
endswiths: "_similar",
nendswiths: "_nsimilar",
contained_in: "_contained_in",
has_key: "_has_key",
has_keys_any: "_has_keys_any",
has_keys_all: "_has_keys_all",
_contains: "_contains",
_contained_in: "_contained_in",
_has_key: "_has_key",
_has_keys_any: "_has_keys_any",
_has_keys_all: "_has_keys_all",
};

export const handleFilterValue = (
Expand Down Expand Up @@ -131,13 +156,15 @@ export const generateNestedFilterQuery = (
const { field, value } = filter;

const defaultNamingConvention = namingConvention === "hasura-default";
const hasuraOperator = defaultNamingConvention
let hasuraOperator = defaultNamingConvention
? hasuraFilters[filter.operator]
: convertHasuraOperatorToGraphqlDefaultNaming(
hasuraFilters[filter.operator],
);
if (!hasuraOperator) {
throw new Error(`Operator ${operator} is not supported`);
hasuraOperator = filter.operator.startsWith("_")
? (filter.operator as HasuraFilterCondition)
: (`_${filter.operator}` as HasuraFilterCondition);
}

const fieldsArray = field.split(".");
Expand Down
7 changes: 7 additions & 0 deletions packages/hasura/test/utils/generateFilters.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,13 @@ describe("handleFilterValue", () => {
["ncontains", "test", "%test%"],
["ncontainss", "test", "%test%"],
["eq", "test", "test"],
["_contains", { a: 1 }, { a: 1 }],
["_contains", ["a", "b"], ["a", "b"]],
["_contained_in", { a: 1 }, { a: 1 }],
["_contained_in", ["a", "b"], ["a", "b"]],
["_has_key", "a", "a"],
["_has_keys_any", ["a", "b"], ["a", "b"]],
["_has_keys_all", ["a", "b"], ["a", "b"]],
];

it.each(testCases)(
Expand Down
Loading