{"_id":"@aginix/ra-data-hasura","_rev":"2-d721a3f66cd5d7b0ac72b16d12cd87af","name":"@aginix/ra-data-hasura","dist-tags":{"latest":"0.7.2"},"versions":{"0.7.1":{"name":"@aginix/ra-data-hasura","version":"0.7.1","keywords":["reactjs","react","react-admin","admin-on-rest","rest","graphql","hasura"],"license":"MIT","_id":"@aginix/ra-data-hasura@0.7.1","maintainers":[{"name":"n3n","email":"nonpawit.tee@gmail.com"}],"homepage":"https://github.com/hasura/ra-data-hasura#readme","bugs":{"url":"https://github.com/hasura/ra-data-hasura/issues"},"dist":{"shasum":"7a53a58cc31ea8594850aee60f07449281e481eb","tarball":"https://registry.npmjs.org/@aginix/ra-data-hasura/-/ra-data-hasura-0.7.1.tgz","fileCount":9,"integrity":"sha512-XRkMRyOep35KqeKDCOlnoN1m52tKWHk4fnBlb1/fEzVbZc8w9j0g3V9b0axSI/XMQ4LXz2CCZhc7uH4nPBS0Lw==","signatures":[{"sig":"MEUCIEI25ygsbDdFf/MpccKwtweoUR/UfTnFsw6GiSSVibzwAiEAvI32C+TRNa/Pg6sZ3xtrsfkChbcpSk8l7cMZLdPk980=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":893017},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","authors":["Praveen Durairaju","Radcliffe Robinson"],"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./package.json":"./package.json"},"gitHead":"7ba16575314fc8d7e08e70c825fce1cf30aa4c69","scripts":{"test":"jest","build":"tsup","prepare":"husky install","prettier":"prettier --config ./.prettierrc --write '**/*.{js,jsx,md}'","prepublishOnly":"npm run test && npm run build"},"_npmUser":{"name":"n3n","email":"nonpawit.tee@gmail.com"},"repository":{"url":"git+https://github.com/hasura/ra-data-hasura.git","type":"git"},"_npmVersion":"11.8.0","description":"A data provider for connecting react-admin to a Hasura endpoint","directories":{},"sideEffects":false,"_nodeVersion":"24.13.1","dependencies":{"graphql":"^16.6.0"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.5.0","tsup":"^8.0.2","husky":"^8.0.2","lodash":"^4.17.21","rimraf":"^3.0.2","ts-jest":"^29.1.0","prettier":"~3.2.5","typescript":"^5.1.3","@types/jest":"^29.5.2","pretty-quick":"^4.0.0","@types/lodash":"^4.14.191","ra-data-graphql":"^5.0.3"},"peerDependencies":{"ra-core":"^5.0.0","ra-data-graphql":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/ra-data-hasura_0.7.1_1777542617596_0.39519801170862223","host":"s3://npm-registry-packages-npm-production"}},"0.7.2":{"name":"@aginix/ra-data-hasura","version":"0.7.2","description":"A data provider for connecting react-admin to a Hasura endpoint","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./package.json":"./package.json"},"sideEffects":false,"repository":{"type":"git","url":"git+https://github.com/hasura/ra-data-hasura.git"},"bugs":{"url":"https://github.com/hasura/ra-data-hasura/issues"},"homepage":"https://github.com/hasura/ra-data-hasura#readme","authors":["Praveen Durairaju","Radcliffe Robinson"],"keywords":["reactjs","react","react-admin","admin-on-rest","rest","graphql","hasura"],"license":"MIT","scripts":{"build":"tsup","prepublishOnly":"npm run test && npm run build","test":"jest","prettier":"prettier --config ./.prettierrc --write '**/*.{js,jsx,md}'","prepare":"husky install"},"dependencies":{"graphql":"^16.6.0"},"peerDependencies":{"ra-core":"^5.0.0","ra-data-graphql":"^5.0.0"},"devDependencies":{"@types/jest":"^29.5.2","@types/lodash":"^4.14.191","husky":"^8.0.2","jest":"^29.5.0","lodash":"^4.17.21","prettier":"~3.2.5","pretty-quick":"^4.0.0","ra-data-graphql":"^5.0.3","rimraf":"^3.0.2","ts-jest":"^29.1.0","tsup":"^8.0.2","typescript":"^5.1.3"},"gitHead":"8e4e19ef2af56f00823abc358c5d2ed62e6d17aa","_id":"@aginix/ra-data-hasura@0.7.2","_nodeVersion":"24.13.1","_npmVersion":"11.8.0","dist":{"integrity":"sha512-F77OWqGgg+MUFLLFNQz0EZmNtUvYANjzcDL8iSvxFB/wuDaurpJvX9dXzEm6VWkiviLXZg53uZ+7iWEgaarckQ==","shasum":"bac8611665ca8400ad7d0f853d0fb4c8e7b97a3a","tarball":"https://registry.npmjs.org/@aginix/ra-data-hasura/-/ra-data-hasura-0.7.2.tgz","fileCount":9,"unpackedSize":917856,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIC9GuI3FDoi/3U3LcOy9qj5ms1rTA7ifSo1AK3YMR2G2AiAWyIrVbk/iSLtnVmuK7go1offM6+sAmmGCIE78yI2CnQ=="}]},"_npmUser":{"name":"n3n","email":"nonpawit.tee@gmail.com"},"directories":{},"maintainers":[{"name":"n3n","email":"nonpawit.tee@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ra-data-hasura_0.7.2_1778041950989_0.48470672418699845"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-30T09:50:17.491Z","modified":"2026-05-06T04:32:31.334Z","0.7.1":"2026-04-30T09:50:17.792Z","0.7.2":"2026-05-06T04:32:31.199Z"},"bugs":{"url":"https://github.com/hasura/ra-data-hasura/issues"},"license":"MIT","homepage":"https://github.com/hasura/ra-data-hasura#readme","keywords":["reactjs","react","react-admin","admin-on-rest","rest","graphql","hasura"],"repository":{"type":"git","url":"git+https://github.com/hasura/ra-data-hasura.git"},"description":"A data provider for connecting react-admin to a Hasura endpoint","maintainers":[{"name":"n3n","email":"nonpawit.tee@gmail.com"}],"readme":"# ra-data-hasura\n\nA GraphQL data provider for [react-admin](https://marmelab.com/react-admin) tailored to target [Hasura](https://hasura.io/) GraphQL endpoints.\n\n| Library version | React Admin version |\n| --------------- | ------------------- |\n| `>= 0.7.0`      | v5                  |\n| `0.5.x – 0.6.x` | v4                  |\n| `<= 0.4.2`      | v3                  |\n\n- [ra-data-hasura](#ra-data-hasura)\n  - [Benefits and Motivation](#benefits-and-motivation)\n  - [Installation](#installation)\n  - [Usage](#usage)\n  - [How It Works](#how-it-works)\n  - [Options](#options)\n    - [Customize the Apollo client](#customize-the-apollo-client)\n    - [Adding Authentication Headers](#adding-authentication-headers)\n    - [Customize the introspection](#customize-the-introspection)\n    - [Customize the Data Return](#customize-the-data-return)\n    - [Debug Mode](#debug-mode)\n  - [Customizing queries](#customizing-queries)\n    - [Example: extending a query to include related entities](#example-extending-a-query-to-include-related-entities)\n    - [Example: write a completely custom query](#example-write-a-completely-custom-query)\n  - [Special Filter Features](#special-filter-features)\n    - [Multi-field OR filtering](#multi-field-or-filtering)\n    - [Nested filtering](#nested-filtering)\n    - [Jsonb filtering](#jsonb-filtering)\n    - [Raw Hasura query filter](#raw-hasura-query-filter)\n    - [Programmatic filters (customFilters)](#programmatic-filters-customfilters)\n  - [Sorting](#sorting)\n    - [Sorting by multiple columns](#sorting-by-multiple-columns)\n    - [Null handling in sort](#null-handling-in-sort)\n  - [Disabling pagination](#disabling-pagination)\n  - [Contributing](#contributing)\n  - [Credits](#credits)\n\nExample applications demonstrating usage:\n\n- [react-admin-low-code](https://github.com/cpursley/react-admin-low-code) (basic usage)\n- [react-admin-hasura-queries](https://github.com/cpv123/react-admin-hasura-queries) (usage with custom queries)\n\n## Benefits and Motivation\n\nThis utility is built on top of [ra-data-graphql](https://github.com/vladimiregorov/react-admin/blob/master/packages/ra-data-graphql/README.md) and is a custom data provider for the current Hasura GraphQL API format.\n\nThe existing ra-data-graphql-simple provider requires that your GraphQL endpoint implement a specific grammar for the objects and methods exposed, which is different with Hasura because the exposed objects and methods are generated differently.\n\nThis utility auto generates valid GraphQL queries based on the properties exposed by the Hasura API such as `object_bool_exp` and `object_set_input`.\n\n## Installation\n\n```sh\nnpm install --save graphql @aginix/ra-data-hasura\n```\n\n## Usage\n\nThe `@aginix/ra-data-hasura` package exposes a single default function with the following signature:\n\n```js\nbuildHasuraProvider(\n  options?: Object,\n  buildGqlQueryOverrides?: Object,\n  customBuildVariables?: Function,\n  customGetResponseParser?: Function,\n) => Promise<DataProvider>\n```\n\nSee the [Options](#options) and [Customizing queries](#customizing-queries) sections below for more details on these arguments.\n\nThis function acts as a constructor for a `dataProvider` based on a Hasura GraphQL endpoint. When executed, this function calls the endpoint, running an [introspection](http://graphql.org/learn/introspection/) query to learn about the specific data models exposed by your Hasura endpoint. It uses the result of this query (the GraphQL schema) to automatically configure the `dataProvider` accordingly.\n\n```jsx\nimport React, { useState, useEffect } from 'react';\nimport buildHasuraProvider from '@aginix/ra-data-hasura';\nimport { Admin, Resource } from 'react-admin';\n\nimport { PostCreate, PostEdit, PostList } from './posts';\n\nconst App = () => {\n  const [dataProvider, setDataProvider] = useState(null);\n\n  useEffect(() => {\n    const buildDataProvider = async () => {\n      const dataProvider = await buildHasuraProvider({\n        clientOptions: { uri: 'http://localhost:8080/v1/graphql' },\n      });\n      setDataProvider(() => dataProvider);\n    };\n    buildDataProvider();\n  }, []);\n\n  if (!dataProvider) return <p>Loading...</p>;\n\n  return (\n    <Admin dataProvider={dataProvider}>\n      <Resource\n        name=\"Post\"\n        list={PostList}\n        edit={PostEdit}\n        create={PostCreate}\n      />\n    </Admin>\n  );\n};\n\nexport default App;\n```\n\n## How It Works\n\nThe data provider converts React Admin queries into the form expected by Hasura's GraphQL API. For example, a React Admin `GET_LIST` request for a person resource with the parameters:\n\n```json\n{\n  \"pagination\": { \"page\": 1, \"perPage\": 5 },\n  \"sort\": { \"field\": \"name\", \"order\": \"DESC\" },\n  \"filter\": {\n    \"ids\": [101, 102]\n  }\n}\n```\n\nwill generate the following GraphQL request for Hasura:\n\n```graphql\nquery person(\n  $limit: Int\n  $offset: Int\n  $order_by: [person_order_by!]!\n  $where: person_bool_exp\n) {\n  items: person(\n    limit: $limit\n    offset: $offset\n    order_by: $order_by\n    where: $where\n  ) {\n    id\n    name\n    address_id\n  }\n  total: person_aggregate(\n    limit: $limit\n    offset: $offset\n    order_by: $order_by\n    where: $where\n  ) {\n    aggregate {\n      count\n    }\n  }\n}\n```\n\nWith the following variables:\n\n```json\n{\n  \"limit\": 5,\n  \"offset\": 0,\n  \"order_by\": { \"name\": \"desc\" },\n  \"where\": {\n    \"_and\": [\n      {\n        \"id\": { \"_in\": [101, 102] }\n      }\n    ]\n  }\n}\n```\n\nReact Admin sort and filter objects will be converted appropriately. For example, sorting with dot notation:\n\n```jsx\nexport const PostList = (props) => (\n  <List {...props} sort={{ field: 'user.email', order: 'DESC' }}>\n    ...\n  </List>\n);\n```\n\nwill generate:\n\n```json\n{ \"order_by\": { \"user\": { \"email\": \"desc\" } } }\n```\n\nand `distinct_on`:\n\n```jsx\nexport const AddressList = () => (\n  <List\n    sort={{ field: 'city', order: 'DESC' }}\n    filter={{ distinct_on: 'city' }}\n  >\n    ...\n  </List>\n);\n```\n\nwill generate:\n\n```json\n{\n  \"order_by\": { \"city\": \"desc\" },\n  \"distinct_on\": \"city\"\n}\n```\n\nKeep in mind that `distinct_on` must be used in conjunction with `order_by`, otherwise a `\"distinct_on\" columns must match initial \"order_by\" columns\"` error will result. See more [here](https://hasura.io/docs/latest/queries/postgres/distinct-queries/#the-distinct_on-argument).\n\n## Options\n\n### Customize the Apollo client\n\nYou can either supply just the client options:\n\n```js\nbuildHasuraProvider({\n  clientOptions: {\n    uri: 'http://localhost:8080/v1/graphql',\n    ...otherApolloOptions,\n  },\n});\n```\n\nor supply the client instance directly:\n\n```js\nbuildHasuraProvider({ client: myClient });\n```\n\n### Adding Authentication Headers\n\nTo send authentication headers, supply the client instance directly with headers defined:\n\n```js\nimport { ApolloClient, InMemoryCache } from '@apollo/client';\n\nconst myClientWithAuth = new ApolloClient({\n  uri: 'http://localhost:8080/v1/graphql',\n  cache: new InMemoryCache(),\n  headers: {\n    'x-hasura-admin-secret': 'hasuraAdminSecret',\n    // 'Authorization': `Bearer xxxx`,\n  },\n});\n\nbuildHasuraProvider({ client: myClientWithAuth });\n```\n\n<details style=\"margin-bottom: 20px\">\n\n<summary style=\"margin-bottom: 10px\">Adding headers using just client options</summary>\n\nYou can also add headers using only client options rather than the client itself:\n\n```js\nimport { createHttpLink } from '@apollo/client';\nimport { setContext } from '@apollo/client/link/context';\n\nconst authLink = setContext((_, { headers }) => ({\n  headers: {\n    ...headers,\n    'x-hasura-admin-secret': 'hasuraAdminSecret',\n    // 'Authorization': `Bearer xxxx`,\n  },\n}));\n\nconst httpLink = createHttpLink({\n  uri: 'http://localhost:8080/v1/graphql',\n});\n\nconst clientOptionsWithAuth = {\n  link: authLink.concat(httpLink),\n};\n\nbuildHasuraProvider({ client: clientOptionsWithAuth });\n```\n\n</details>\n\n### Customize the introspection\n\nThese are the default options for introspection:\n\n```js\nconst introspectionOptions = {\n  include: [], // Either an array of types to include or a function which will be called for every type discovered through introspection\n  exclude: [], // Either an array of types to exclude or a function which will be called for every type discovered through introspection\n};\n\n// Including types\nconst introspectionOptions = {\n  include: ['Post', 'Comment'],\n};\n\n// Excluding types\nconst introspectionOptions = {\n  exclude: ['CommandItem'],\n};\n\n// Including types with a function\nconst introspectionOptions = {\n  include: (type) => ['Post', 'Comment'].includes(type.name),\n};\n\n// Excluding types with a function\nconst introspectionOptions = {\n  exclude: (type) => !['Post', 'Comment'].includes(type.name),\n};\n```\n\n**Note**: `exclude` and `include` are mutually exclusive and `include` will take precedence.\n\n**Note**: When using functions, the `type` argument will be a type returned by the introspection query. Refer to the [introspection](http://graphql.org/learn/introspection/) documentation for more information.\n\nPass the introspection options to the `buildHasuraProvider` function:\n\n```js\nbuildHasuraProvider({ introspection: introspectionOptions });\n```\n\n### Customize the Data Return\n\nOnce the data is returned back from the provider, you can customize it by implementing the `DataProvider` interface. [An example is changing the ID key](https://marmelab.com/react-admin/FAQ.html#can-i-have-custom-identifiersprimary-keys-for-my-resources).\n\n```typescript\nconst [dataProvider, setDataProvider] = React.useState<DataProvider | null>(\n  null\n);\n\nReact.useEffect(() => {\n  const buildDataProvider = async () => {\n    const dataProviderHasura = await buildHasuraProvider({\n      clientOptions: {\n        uri: 'http://localhost:8080/v1/graphql',\n      },\n    });\n    const modifiedProvider: DataProvider = {\n      getList: async (resource, params) => {\n        let { data, ...metadata } = await dataProviderHasura.getList(\n          resource,\n          params\n        );\n\n        if (resource === 'example_resource_name') {\n          data = data.map(\n            (val): Record => ({\n              ...val,\n              id: val.region_id,\n            })\n          );\n        }\n\n        return { data: data as any[], ...metadata };\n      },\n      getOne: (resource, params) => dataProviderHasura.getOne(resource, params),\n      getMany: (resource, params) =>\n        dataProviderHasura.getMany(resource, params),\n      getManyReference: (resource, params) =>\n        dataProviderHasura.getManyReference(resource, params),\n      update: (resource, params) => dataProviderHasura.update(resource, params),\n      updateMany: (resource, params) =>\n        dataProviderHasura.updateMany(resource, params),\n      create: (resource, params) => dataProviderHasura.create(resource, params),\n      delete: (resource, params) => dataProviderHasura.delete(resource, params),\n      deleteMany: (resource, params) =>\n        dataProviderHasura.deleteMany(resource, params),\n    };\n    setDataProvider(() => modifiedProvider);\n  };\n  buildDataProvider();\n}, []);\n```\n\n### Debug Mode\n\nPass `debug: true` to log every request to the browser console. Each call is rendered as a collapsible group with its `fetchType`, params, the printed GraphQL query, variables, response (or error), and duration. Requests are tagged with a sequential id (`#1`, `#2`, …) so request and response groups stay correlated even when calls interleave. Schema introspection is also logged the first time it runs.\n\n```ts\nconst dataProvider = await buildHasuraProvider({\n  client: apolloClient,\n  debug: true,\n});\n```\n\n> [!WARNING]\n> Debug mode prints **everything sent to and received from Hasura**, including mutation variables (e.g. password hashes, tokens, or any other sensitive column values). Only enable it in development. Gate it behind an environment check before shipping:\n>\n> ```ts\n> debug: process.env.NODE_ENV === 'development',\n> ```\n\n## Customizing queries\n\nQueries built by this data provider are made up of 3 parts:\n\n1. The set of fields requested\n2. The variables defining the query constraints like `where, order_by, limit, offset`\n3. The response format e.g. `{ data: {...}, total: 100 }`\n\nEach of these can be customized — functions overriding numbers 2 and 3 can be passed directly to `buildHasuraProvider` as shown in [Usage](#usage), whilst number 1 can be customized in parts using the `buildGqlQueryOverrides` object argument:\n\n```js\n{\n  buildFields?: Function,\n  buildMetaArgs?: Function,\n  buildArgs?: Function,\n  buildApolloArgs?: Function,\n}\n```\n\nA likely scenario is that you want to override only the `buildFields` part so that you can customize your GraphQL queries — requesting fewer fields, more fields, nested fields etc.\n\nThis can be easily done, and importantly can be done using `gql` template literal tags, as shown in the examples below. Take a look at this [demo application](https://github.com/cpv123/react-admin-hasura-queries) to see it in action.\n\n### Example: extending a query to include related entities\n\nBy default, the data provider will generate queries that include all fields on a resource, but without any relationships to nested entities. If you would like to keep these base fields but extend the query to also include related entities, then you can write a custom `buildFields` like this:\n\n```ts\nimport buildDataProvider, { buildFields } from '@aginix/ra-data-hasura';\nimport type { BuildFields } from '@aginix/ra-data-hasura';\nimport gql from 'graphql-tag';\n\nconst extractFieldsFromQuery = (queryAst) => {\n  return queryAst.definitions[0].selectionSet.selections;\n};\n\nconst EXTENDED_GET_ONE_USER = gql`\n  {\n    todos_aggregate {\n      aggregate {\n        count\n      }\n    }\n  }\n`;\n\nconst customBuildFields: BuildFields = (type, fetchType) => {\n  const resourceName = type.name;\n\n  const defaultFields = buildFields(type, fetchType);\n\n  if (resourceName === 'users' && fetchType === 'GET_ONE') {\n    const relatedEntities = extractFieldsFromQuery(EXTENDED_GET_ONE_USER);\n    defaultFields.push(...relatedEntities);\n  }\n\n  return defaultFields;\n};\n\nbuildDataProvider(options, { buildFields: customBuildFields });\n```\n\n### Example: write a completely custom query\n\nIf you want full control over the GraphQL query, then you can define the entire set of fields like this:\n\n```ts\nimport gql from 'graphql-tag';\nimport buildDataProvider, { buildFields } from '@aginix/ra-data-hasura';\nimport type { BuildFields } from '@aginix/ra-data-hasura';\n\nconst extractFieldsFromQuery = (queryAst) => {\n  return queryAst.definitions[0].selectionSet.selections;\n};\n\nconst GET_ONE_USER = gql`\n  {\n    id\n    name\n    todos(\n      where: { is_completed: { _eq: false } }\n      order_by: { created_at: asc }\n    ) {\n      title\n    }\n    todos_aggregate {\n      aggregate {\n        count\n      }\n    }\n  }\n`;\n\nconst customBuildFields: BuildFields = (type, fetchType) => {\n  const resourceName = type.name;\n\n  if (resourceName === 'users' && fetchType === 'GET_ONE') {\n    return extractFieldsFromQuery(GET_ONE_USER);\n  }\n\n  return buildFields(type, fetchType);\n};\n\nbuildDataProvider(options, { buildFields: customBuildFields });\n```\n\nNote that when using this approach in particular, it is possible that you will come across [this issue](https://github.com/cpv123/react-admin-hasura-queries#troubleshooting).\n\n## Special Filter Features\n\nThis adapter provides a rich filter syntax using special key patterns. Keys are parsed using:\n\n- `@` as operator separator (e.g. `field@_ilike`)\n- `#` as nested field separator (e.g. `relation#field`)\n- `,` to create OR conditions across multiple fields\n\nThe default comparator is `_ilike` for strings (automatically wraps value in `%value%`) and `_eq` for other types.\n\n### Multi-field OR filtering\n\nComma-separate multiple field paths in a single `source` to produce an `_or` condition:\n\n```tsx\n<Filter {...props}>\n  <TextInput\n    label=\"Search\"\n    source=\"email,first_name@_eq,last_name@_like\"\n    alwaysOn\n  />\n</Filter>\n```\n\nGenerates:\n\n```json\n{\n  \"where\": {\n    \"_or\": [\n      { \"email\": { \"_ilike\": \"%edu%\" } },\n      { \"first_name\": { \"_eq\": \"edu\" } },\n      { \"last_name\": { \"_like\": \"%edu%\" } }\n    ]\n  }\n}\n```\n\n### Nested filtering\n\nUse `#` as a field separator to filter on related object fields:\n\n```tsx\n<TextInput\n  label=\"Search by indication, drug, sponsor, nctid\"\n  source=\"indication#name@_ilike,drug#preferred_name@_ilike,sponsor#name@_ilike,trial#nctid@_ilike\"\n  alwaysOn\n/>\n```\n\nGenerates:\n\n```json\n{\n  \"where\": {\n    \"_or\": [\n      { \"indication\": { \"name\": { \"_ilike\": \"%TEXT%\" } } },\n      { \"drug\": { \"name\": { \"_ilike\": \"%TEXT%\" } } },\n      { \"sponsor\": { \"name\": { \"_ilike\": \"%TEXT%\" } } }\n    ]\n  }\n}\n```\n\n### Jsonb filtering\n\nUse `@_contains` with a `#`-separated path to filter on JSONB fields:\n\n```jsx\n<TextField label=\"Theme Color\" source=\"users#preferences@_contains@ux#theme\" />\n```\n\nGenerates:\n\n```json\n{\n  \"where\": {\n    \"_and\": [\n      {\n        \"users\": {\n          \"preferences\": {\n            \"_contains\": { \"ux\": { \"theme\": \"%TEXT\" } }\n          }\n        }\n      }\n    ]\n  }\n}\n```\n\nDynamic JSONB filtering using a related record field:\n\n```jsx\n<FunctionField\n  render={(rec) => (\n    <ReferenceManyField\n      reference=\"account_plans\"\n      target={`payments#details@_contains@processor#${rec.processor}_id`}\n      source=\"payment_processor\"\n    >\n      <Datagrid>...</Datagrid>\n    </ReferenceManyField>\n  )}\n/>\n```\n\n### Raw Hasura query filter\n\nWhen the standard filter syntax cannot express your condition, you can pass a raw Hasura `where` object directly using the `hasura-raw-query` format. This bypasses all filter processing and injects the value as-is into the `where` clause.\n\n```tsx\n// In a custom List component or hook:\nconst filters = {\n  status: {\n    format: 'hasura-raw-query',\n    value: { _in: ['active', 'pending'] },\n  },\n};\n\n<List filter={filters}>...</List>;\n```\n\nThis is especially useful when you need to express conditions that the `@` / `#` syntax does not cover, such as `_nin`, `_similar`, or nested `_and`/`_or` logic:\n\n```ts\nconst filters = {\n  metadata: {\n    format: 'hasura-raw-query',\n    value: {\n      _or: [{ tags: { _contains: 'featured' } }, { priority: { _gte: 5 } }],\n    },\n  },\n};\n```\n\nThe key (`status`, `metadata`, etc.) is still used as the field path. To inject a condition at the top level of `_and`, use a key that matches the desired root field.\n\n### Programmatic filters (customFilters)\n\nYou can pass additional pre-built filter objects via `customFilters` on the params object. These are merged directly into the `_and` array alongside the standard `filter` object:\n\n```tsx\nimport { useListController } from 'react-admin';\n\nconst MyList = () => {\n  const controllerProps = useListController({\n    resource: 'posts',\n    // customFilters are appended to the _and clause\n    filter: { status: 'published' },\n    // @ts-ignore — customFilters is not part of the official RA type\n    customFilters: [{ author_id: { _eq: currentUserId } }],\n  });\n  // ...\n};\n```\n\n`customFilters` is an array of raw Hasura filter objects. Each object is added as an additional `_and` condition alongside any filters derived from the `filter` param.\n\n## Sorting\n\n### Sorting by multiple columns\n\nHasura supports [sorting by multiple fields](https://hasura.io/docs/latest/graphql/core/databases/postgres/queries/sorting.html#sorting-by-multiple-fields). Since React Admin's `List` `sort` prop does not accept arrays, separate multiple fields and orders with commas:\n\n```jsx\nconst TodoList = (props) => (\n  <List sort={{ field: 'title,is_completed', order: 'asc,desc' }} {...props}>\n    <Datagrid rowClick=\"edit\">...</Datagrid>\n  </List>\n);\n```\n\ngenerates:\n\n```json\n{ \"order_by\": [{ \"title\": \"asc\" }, { \"is_completed\": \"desc\" }] }\n```\n\nFields may contain dots to sort by nested object properties (e.g. `user.email`).\n\n### Null handling in sort\n\nAppend `@nulls_last` or `@nulls_first` to a sort field to control how `NULL` values are ordered:\n\n```jsx\n<List sort={{ field: 'published_at@nulls_last', order: 'DESC' }}>...</List>\n```\n\ngenerates:\n\n```json\n{ \"order_by\": { \"published_at\": \"desc_nulls_last\" } }\n```\n\nSupported modifiers: `nulls_last`, `nulls_first`.\n\n## Disabling pagination\n\nSet `perPage` to `-1` to fetch all records without a `limit` or `offset` being sent to Hasura:\n\n```jsx\n<List pagination={false} perPage={-1}>\n  ...\n</List>\n```\n\nUse with caution on large tables.\n\n## Contributing\n\nTo modify, extend and test this package locally:\n\n```sh\ncd ra-data-hasura\nnpm link\n```\n\nNow use this local package in your React app for testing:\n\n```sh\ncd my-react-app\nnpm link @aginix/ra-data-hasura\n```\n\nBuild the library by running `npm run build` — output is generated in the `dist` folder.\n\n## Credits\n\nWe would like to thank [Steams](https://github.com/Steams) and all the contributors to this library for porting this adapter to support GraphQL spec, since all the releases till v0.0.8 were based off the REST API spec.\n","readmeFilename":"README.md"}