rival-black
rival-black8mo ago

Not getting useList with Hasura as GraphQL provider to work

This is my dataProvider:
import graphqlDataProvider, {
GraphQLClient,
liveProvider as graphqlLiveProvider,
graphqlWS,
} from "@refinedev/hasura";

// Update the GraphQL client initialization to use the Hasura endpoint URL
export const API_BASE_URL = import.meta.env.VITE_HASURA_BASE_URL;
export const API_URL = `${API_BASE_URL}/v1/graphql`;
export const WS_URL = import.meta.env.VITE_HASURA_WS_URL;

export const client = new GraphQLClient(API_URL, {
headers: {
"x-hasura-role": "admin",
"x-hasura-admin-secret": "xxx"
},
});

export const wsClient = graphqlWS.createClient({
url: WS_URL,
});

export const dataProvider = graphqlDataProvider(client, { namingConvention: "graphql-default" });
import graphqlDataProvider, {
GraphQLClient,
liveProvider as graphqlLiveProvider,
graphqlWS,
} from "@refinedev/hasura";

// Update the GraphQL client initialization to use the Hasura endpoint URL
export const API_BASE_URL = import.meta.env.VITE_HASURA_BASE_URL;
export const API_URL = `${API_BASE_URL}/v1/graphql`;
export const WS_URL = import.meta.env.VITE_HASURA_WS_URL;

export const client = new GraphQLClient(API_URL, {
headers: {
"x-hasura-role": "admin",
"x-hasura-admin-secret": "xxx"
},
});

export const wsClient = graphqlWS.createClient({
url: WS_URL,
});

export const dataProvider = graphqlDataProvider(client, { namingConvention: "graphql-default" });
This is my query:
export const DASHBOARD_TASKS_CHART_QUERY = gql`
query DashboardTasksChart(
$where: TaskStagesBoolExp!
$orderBy: [TaskStagesOrderBy!]
$limit: Int!
$offset: Int!
) {
taskStages(where: $where, orderBy: $orderBy, limit: $limit, offset: $offset) {
title
tasksAggregate {
aggregate {
count(columns: id)
}
}
}
}
`;
export const DASHBOARD_TASKS_CHART_QUERY = gql`
query DashboardTasksChart(
$where: TaskStagesBoolExp!
$orderBy: [TaskStagesOrderBy!]
$limit: Int!
$offset: Int!
) {
taskStages(where: $where, orderBy: $orderBy, limit: $limit, offset: $offset) {
title
tasksAggregate {
aggregate {
count(columns: id)
}
}
}
}
`;
24 Replies
kapa.ai
kapa.ai8mo ago
Hi @Henrik. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- It looks like you're encountering issues with the useList hook when using Hasura as your GraphQL provider with the graphql-default naming convention. This is a known issue and has been discussed in the community. Issue Description When using the graphql-default naming convention, the useList hook may fail due to the way the aggregate operation is handled. Specifically, the getList function in the Hasura provider expects a certain structure that may not align with the graphql-default naming convention. Solution To resolve this issue, you can modify the Hasura provider to correctly handle the graphql-default naming convention. This involves ensuring that the aggregate operation is correctly camelized. Steps to Fix 1. Modify the Hasura Provider: You need to adjust the Hasura provider to handle the graphql-default naming convention correctly. This can be done by swizzling the provider to use a camelized aggregate operation. 2. Example Configuration: Here is an example of how you can configure your project to use the graphql-default naming convention:
npm create refine-app@latest
Downloaded remote source successfully.
Choose a project template · refine-vite
What would you like to name your project?: · metal-goats-flash
Choose your backend service to connect: · data-provider-hasura
Do you want to use a UI Framework?: · mui
Do you want to add example pages?: · no
Do you need any Authentication logic?: · none
Do you need i18n (Internationalization) support?: · no
Choose a package manager: · npm

npm create refine-app@latest
Downloaded remote source successfully.
Choose a project template · refine-vite
What would you like to name your project?: · metal-goats-flash
Choose your backend service to connect: · data-provider-hasura
Do you want to use a UI Framework?: · mui
Do you want to add example pages?: · no
Do you need any Authentication logic?: · none
Do you need i18n (Internationalization) support?: · no
Choose a package manager: · npm

3. Set Naming Convention: Add the namingConvention settings to the Hasura provider in your src/App.tsx:
<Refine
- dataProvider={dataProvider(client)}
+ dataProvider={dataProvider(client, { namingConvention: "graphql-default" })}

<Refine
- dataProvider={dataProvider(client)}
+ dataProvider={dataProvider(client, { namingConvention: "graphql-default" })}

Additional Resources - Hasura Provider Example (https://refine.dev/docs/data/packages/hasura/#example) - DataProvider Tutorial (https://refine.dev/docs/tutorial/understanding-dataprovider/index/) Relevant Issues and Discussions - Github Issue #4574 (https://github.com/refinedev/refine/issues/4574): Discusses the problem with list retrieval when using the graphql-default naming convention. - Github Issue #3828 (https://github.com/refinedev/refine/issues/3828): Discusses issues related to the DataProvider configuration. By following these steps, you should be able to resolve the issues with the useList hook when using Hasura as your GraphQL provider with the graphql-default naming convention. If you continue to experience issues, consider reviewing the linked issues and discussions for more context and potential solutions. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai8mo ago
<@135097094825443328> kapa.ai is still learning and improving, please let us know how it did by reacting below
rival-black
rival-blackOP8mo ago
@kapa.ai This is the implementation with useList
export const DashboardTasksChart: React.FC = () => {
const { list } = useNavigation();
const { data, isError, error } = useList<
GetFieldsFromList<DashboardTasksChartQuery>
>({
resource: "taskStages",
pagination: {
pageSize: 4,
},
filters: [
{
field: "title",
operator: "contains",
value: "",
},
],
sorters: [
{
field: "title",
order: "asc",
},
],
meta: {
gqlQuery: DASHBOARD_TASKS_CHART_QUERY
},
});

if (isError) {
console.error("Error fetching task chart data", error);
return null;
}

// console.log("Fetched data:", data);

const tasksData = useMemo(() => {
if (!data?.data?.length) {
return [];
}

return data.data
.map((stage) => ({
title: stage.title,
value: stage.tasksAggregate?.aggregate?.count ?? 0,
// value: stage.tasksAggregate?.[0]?.count?.id ?? 0,
}))
.filter(
(stage) =>
stage.value !== null && stage.value !== undefined && stage.value > 0,
)
.sort((a, b) => (b.value ?? 0) - (a.value ?? 0));
}, [data?.data]);
export const DashboardTasksChart: React.FC = () => {
const { list } = useNavigation();
const { data, isError, error } = useList<
GetFieldsFromList<DashboardTasksChartQuery>
>({
resource: "taskStages",
pagination: {
pageSize: 4,
},
filters: [
{
field: "title",
operator: "contains",
value: "",
},
],
sorters: [
{
field: "title",
order: "asc",
},
],
meta: {
gqlQuery: DASHBOARD_TASKS_CHART_QUERY
},
});

if (isError) {
console.error("Error fetching task chart data", error);
return null;
}

// console.log("Fetched data:", data);

const tasksData = useMemo(() => {
if (!data?.data?.length) {
return [];
}

return data.data
.map((stage) => ({
title: stage.title,
value: stage.tasksAggregate?.aggregate?.count ?? 0,
// value: stage.tasksAggregate?.[0]?.count?.id ?? 0,
}))
.filter(
(stage) =>
stage.value !== null && stage.value !== undefined && stage.value > 0,
)
.sort((a, b) => (b.value ?? 0) - (a.value ?? 0));
}, [data?.data]);
I get error: tasks-chart.tsx:45 Error fetching task chart data TypeError: Cannot read properties of undefined (reading 'aggregate')
kapa.ai
kapa.ai8mo ago
Let me check the knowledge base for you. One moment! :books:
rival-black
rival-blackOP8mo ago
@kapa.ai This is the response in network console (as expected):
{
"data": {
"taskStages": [
{
"title": "active",
"tasksAggregate": {
"aggregate": {
"count": 1
}
}
},
{
"title": "inactive",
"tasksAggregate": {
"aggregate": {
"count": 1
}
}
}
]
}
}
{
"data": {
"taskStages": [
{
"title": "active",
"tasksAggregate": {
"aggregate": {
"count": 1
}
}
},
{
"title": "inactive",
"tasksAggregate": {
"aggregate": {
"count": 1
}
}
}
]
}
}
kapa.ai
kapa.ai8mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- It looks like the error you're encountering is due to the aggregate property being undefined in the response. This is a known issue when using the graphql-default naming convention with Hasura, as discussed in Github Issue #4574 (https://github.com/refinedev/refine/issues/4574). Solution To resolve this issue, you need to ensure that the aggregate operation is correctly camelized in the Hasura provider. Here’s how you can adjust your implementation: 1. Modify the Hasura Provider: Ensure that the dataProvider is correctly configured to use the graphql-default naming convention:
import graphqlDataProvider, {
GraphQLClient,
liveProvider as graphqlLiveProvider,
graphqlWS,
} from "@refinedev/hasura";

export const API_BASE_URL = import.meta.env.VITE_HASURA_BASE_URL;
export const API_URL = `${API_BASE_URL}/v1/graphql`;
export const WS_URL = import.meta.env.VITE_HASURA_WS_URL;

export const client = new GraphQLClient(API_URL, {
headers: {
"x-hasura-role": "admin",
"x-hasura-admin-secret": "xxx"
},
});

export const wsClient = graphqlWS.createClient({
url: WS_URL,
});

export const dataProvider = graphqlDataProvider(client, { namingConvention: "graphql-default" });

import graphqlDataProvider, {
GraphQLClient,
liveProvider as graphqlLiveProvider,
graphqlWS,
} from "@refinedev/hasura";

export const API_BASE_URL = import.meta.env.VITE_HASURA_BASE_URL;
export const API_URL = `${API_BASE_URL}/v1/graphql`;
export const WS_URL = import.meta.env.VITE_HASURA_WS_URL;

export const client = new GraphQLClient(API_URL, {
headers: {
"x-hasura-role": "admin",
"x-hasura-admin-secret": "xxx"
},
});

export const wsClient = graphqlWS.createClient({
url: WS_URL,
});

export const dataProvider = graphqlDataProvider(client, { namingConvention: "graphql-default" });

2. Adjust the Query and Data Handling: Ensure that your query and data handling logic correctly account for the graphql-default naming convention. Here’s your updated component: ``typescript import React, { useMemo } from "react"; import { useList } from "@refinedev/core"; import { gql } from "graphql-tag"; import { DashboardTasksChartQuery } from "interfaces"; // Adjust the import as necessary export const DASHBOARD_TASKS_CHART_QUERY = gql query DashboardTasksChart( $where: TaskStagesBoolExp! $orderBy: [TaskStagesOrderBy!] $limit: Int! $offset: Int! ) { taskStages(where: $where, orderBy: $orderBy, limit: $limit, offset: $offset) { title tasksAggregate { aggregate { count(columns: id) } } } } `; export const DashboardTasksChart: React.FC = () => { const { data, isError, error } = useList<DashboardTasksChartQuery>({ resource: "taskStages", pagination: { pageSize: 4, }, filters: [ { field: "title", operator: "contains", value: "", }, ], sorters: [ { field: "title", order: "asc", }, ], meta: { gqlQuery: DASHBOARD_TASKS_CHART_QUERY }, }); if (isError) { console.error("Error fetching task chart data", error); return null; } const tasksData = useMemo(() => { if (!data?.data?.length) { return []; } return data.data .map((stage) => ({ title: stage.title, value: stage.tasksAggregate?.aggregate?.count ?? 0, })) .filter( (stage) => stage.value !== null && stage.value !== undefined && stage.value > 0, ) .sort((a, b) => (b.value ?? 0) - (a.value ?? 0)); }, ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai8mo ago
<@135097094825443328> kapa.ai is still learning and improving, please let us know how it did by reacting below
kapa.ai
kapa.ai8mo ago
---------------------- Based on the response you provided from the network console, it looks like the GraphQL query is returning the expected data structure. However, the issue might be related to how the useList hook is interpreting this response. Steps to Ensure Compatibility 1. Check Data Provider Configuration: Ensure that your data provider is correctly configured to handle the graphql-default naming convention. This involves making sure that the aggregate operations are correctly camelized.
import graphqlDataProvider, {
GraphQLClient,
liveProvider as graphqlLiveProvider,
graphqlWS,
} from "@refinedev/hasura";

export const API_BASE_URL = import.meta.env.VITE_HASURA_BASE_URL;
export const API_URL = `${API_BASE_URL}/v1/graphql`;
export const WS_URL = import.meta.env.VITE_HASURA_WS_URL;

export const client = new GraphQLClient(API_URL, {
headers: {
"x-hasura-role": "admin",
"x-hasura-admin-secret": "xxx"
},
});

export const wsClient = graphqlWS.createClient({
url: WS_URL,
});

export const dataProvider = graphqlDataProvider(client, { namingConvention: "graphql-default" });

import graphqlDataProvider, {
GraphQLClient,
liveProvider as graphqlLiveProvider,
graphqlWS,
} from "@refinedev/hasura";

export const API_BASE_URL = import.meta.env.VITE_HASURA_BASE_URL;
export const API_URL = `${API_BASE_URL}/v1/graphql`;
export const WS_URL = import.meta.env.VITE_HASURA_WS_URL;

export const client = new GraphQLClient(API_URL, {
headers: {
"x-hasura-role": "admin",
"x-hasura-admin-secret": "xxx"
},
});

export const wsClient = graphqlWS.createClient({
url: WS_URL,
});

export const dataProvider = graphqlDataProvider(client, { namingConvention: "graphql-default" });

2. Ensure Correct Query Structure: Make sure your query is structured correctly to match the expected response format.
import { gql } from "@apollo/client";
export const DASHBOARD_TASKS_CHART_QUERY = gql`
query DashboardTasksChart(
$where: TaskStagesBoolExp!
$orderBy: [TaskStagesOrderBy!]
$limit: Int!
$offset: Int!
) {
taskStages(where: $where, orderBy: $orderBy, limit: $limit, offset: $offset) {
title
tasksAggregate {
aggregate {
count(columns: id)
}
}
}
}
`;

import { gql } from "@apollo/client";
export const DASHBOARD_TASKS_CHART_QUERY = gql`
query DashboardTasksChart(
$where: TaskStagesBoolExp!
$orderBy: [TaskStagesOrderBy!]
$limit: Int!
$offset: Int!
) {
taskStages(where: $where, orderBy: $orderBy, limit: $limit, offset: $offset) {
title
tasksAggregate {
aggregate {
count(columns: id)
}
}
}
}
`;

3. Handling the Response in useList: Ensure that the useList hook is correctly interpreting the response. You might need to map the response to the expected format if there are discrepancies. Example Usage of useList:
import { useList } from "@refinedev/core";

const { data, isLoading, error } = useList({
resource: "taskStages",
pagination: {
current: 1,
pageSize: 10,
},
sort: [
{
field: "title",
order: "asc",
},
],
filters: [],
metaData: {
fields: ["title", "tasksAggregate.aggregate.count"],
},
});

if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;

return (
<div>
{data?.data.map((item) => (
<div key={item.title}>
<h3>{item.title}</h3>
<p>Count: {item.tasksAggregate.aggregate.count}</p>
</div>
))}
</div>
);
import { useList } from "@refinedev/core";

const { data, isLoading, error } = useList({
resource: "taskStages",
pagination: {
current: 1,
pageSize: 10,
},
sort: [
{
field: "title",
order: "asc",
},
],
filters: [],
metaData: {
fields: ["title", "tasksAggregate.aggregate.count"],
},
});

if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;

return (
<div>
{data?.data.map((item) => (
<div key={item.title}>
<h3>{item.title}</h3>
<p>Count: {item.tasksAggregate.aggregate.count}</p>
</div>
))}
</div>
);
Additional Resources - Hasura Provider Example (https://refine.dev/docs/data/packages/hasura/#example) - DataProvider Tutorial (https://refine.dev/docs/tutorial/understanding-dataprovider/index/) By following these steps, you should be able to ensure that the useList hook works correctly with your Hasura GraphQL provider. If you continue to experience issues, consider reviewing the linked resources for more context and potential solutions. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai8mo ago
<@135097094825443328> kapa.ai is still learning and improving, please let us know how it did by reacting below
rival-black
rival-blackOP8mo ago
@kapa.ai This is the type: export type DashboardTasksChartQuery = { taskStages: Array< Pick<Types.TaskStages, "title"> & { tasksAggregate: { aggregate?: Types.Maybe<Pick<Types.TasksAggregateFields, "count">>; }; } >; }; Why is useList with GetFieldsFromList reading aggregate? export const DashboardTasksChart: React.FC = () => { const { list } = useNavigation(); const { data, isError, error } = useList< GetFieldsFromList<DashboardTasksChartQuery> >({ resource: "taskStages", pagination: { pageSize: 4, }, filters: [ { field: "title", operator: "contains", value: "", }, ], sorters: [ { field: "title", order: "asc", }, ], meta: { gqlQuery: DASHBOARD_TASKS_CHART_QUERY }, });
if (isError) { console.error("Error fetching task chart data", error); return null; }
// console.log("Fetched data:", data); const tasksData = useMemo(() => { if (!data?.data?.length) { return []; }
kapa.ai
kapa.ai8mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- The issue you're encountering with useList and GetFieldsFromList reading the aggregate property might be due to the type mismatch or incorrect usage of GetFieldsFromList. Understanding GetFieldsFromList The GetFieldsFromList utility type is used to extract fields from a GraphQL query result type. However, it seems that in your case, the aggregate field is being read incorrectly. Correct Usage of useList To ensure that useList works correctly with your GraphQL query and types, you should directly use the type DashboardTasksChartQuery instead of GetFieldsFromList. Example Implementation Here’s how you can adjust your implementation: 1. Define the Query Type: Ensure that your query type is correctly defined to match the GraphQL response.
export type DashboardTasksChartQuery = {
taskStages: Array<
Pick<Types.TaskStages, "title"> & {
tasksAggregate: {
aggregate?: Types.Maybe<Pick<Types.TasksAggregateFields, "count">>;
};
}
>;
};

export type DashboardTasksChartQuery = {
taskStages: Array<
Pick<Types.TaskStages, "title"> & {
tasksAggregate: {
aggregate?: Types.Maybe<Pick<Types.TasksAggregateFields, "count">>;
};
}
>;
};

2. Use useList with the Correct Type: Use the DashboardTasksChartQuery type directly in the useList hook. ``typescript import React, { useMemo } from "react"; import { useList } from "@refinedev/core"; import { gql } from "@apollo/client"; import { DashboardTasksChartQuery } from "interfaces"; // Adjust the import as necessary export const DASHBOARD_TASKS_CHART_QUERY = gql query DashboardTasksChart( $where: TaskStagesBoolExp! $orderBy: [TaskStagesOrderBy!] $limit: Int! $offset: Int! ) { taskStages(where: $where, orderBy: $orderBy, limit: $limit, offset: $offset) { title tasksAggregate { aggregate { count(columns: id) } } } } `; export const DashboardTasksChart: React.FC = () => { const { data, isError, error } = useList<DashboardTasksChartQuery>({ resource: "taskStages", pagination: { pageSize: 4, }, filters: [ { field: "title", operator: "contains", value: "", }, ], sorters: [ { field: "title", order: "asc", }, ], meta: { gqlQuery: DASHBOARD_TASKS_CHART_QUERY }, }); if (isError) { console.error("Error fetching task chart data", error); return null; } const tasksData = useMemo(() => { if (!data?.data?.taskStages?.length) { return []; } return data.data.taskStages .map((stage) => ({ title: stage.title, value: stage.tasksAggregate?.aggregate?.count ?? 0, })) .filter( (stage) => stage.value !== null && stage.value !== undefined && stage.value > 0, ) .sort((a, b) => (b.value ?? 0) - (a.value ?? 0)); }, [data?.data]); return ( <div> {tasksData.map((task) => ( <div key={task.title}> <h3>{task.title}</h3> <p>Count: {task.value}</p> </div> ))} </div> ); ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai8mo ago
<@135097094825443328> kapa.ai is still learning and improving, please let us know how it did by reacting below
rival-black
rival-blackOP8mo ago
@kapa.ai What is the expected output, when using Hasura as data provider for useList<GetFieldsFromList<>> ?
kapa.ai
kapa.ai8mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- When using Hasura as a data provider for useList<GetFieldsFromList<>>, the expected output should be a normalized list of items based on the fields specified in your GraphQL query. The GetFieldsFromList utility type is used to extract the fields from your offset-paginated list queries. Example Query and Type Here’s an example of a GraphQL query and the corresponding TypeScript type:
export const DASHBOARD_TASKS_CHART_QUERY = gql`
query DashboardTasksChart(
$where: TaskStagesBoolExp!
$orderBy: [TaskStagesOrderBy!]
$limit: Int!
$offset: Int!
) {
taskStages(where: $where, orderBy: $orderBy, limit: $limit, offset: $offset) {
title
tasksAggregate {
aggregate {
count(columns: id)
}
}
}
}
`;

export type DashboardTasksChartQuery = {
taskStages: Array<
Pick<Types.TaskStages, "title"> & {
tasksAggregate: {
aggregate?: Types.Maybe<Pick<Types.TasksAggregateFields, "count">>;
};
}
>;
};
export const DASHBOARD_TASKS_CHART_QUERY = gql`
query DashboardTasksChart(
$where: TaskStagesBoolExp!
$orderBy: [TaskStagesOrderBy!]
$limit: Int!
$offset: Int!
) {
taskStages(where: $where, orderBy: $orderBy, limit: $limit, offset: $offset) {
title
tasksAggregate {
aggregate {
count(columns: id)
}
}
}
}
`;

export type DashboardTasksChartQuery = {
taskStages: Array<
Pick<Types.TaskStages, "title"> & {
tasksAggregate: {
aggregate?: Types.Maybe<Pick<Types.TasksAggregateFields, "count">>;
};
}
>;
};
Using useList with GetFieldsFromList When you use useList<GetFieldsFromList<DashboardTasksChartQuery>>, the expected output should be a list of taskStages with the fields title and tasksAggregate.aggregate.count. Example Implementation Here’s how you can implement useList with GetFieldsFromList: ``typescript import React, { useMemo } from "react"; import { useList } from "@refinedev/core"; import { gql } from "@apollo/client"; import { DashboardTasksChartQuery } from "interfaces"; // Adjust the import as necessary export const DASHBOARD_TASKS_CHART_QUERY = gql query DashboardTasksChart( $where: TaskStagesBoolExp! $orderBy: [TaskStagesOrderBy!] $limit: Int! $offset: Int! ) { taskStages(where: $where, orderBy: $orderBy, limit: $limit, offset: $offset) { title tasksAggregate { aggregate { count(columns: id) } } } } `; export const DashboardTasksChart: React.FC = () => { const { data, isError, error } = useList<GetFieldsFromList<DashboardTasksChartQuery>>({ resource: "taskStages", pagination: { pageSize: 4, }, filters: [ { field: "title", operator: "contains", value: "", }, ], sorters: [ { field: "title", order: "asc", }, ], meta: { gqlQuery: DASHBOARD_TASKS_CHART_QUERY }, }); if (isError) { console.error("Error fetching task chart data", error); return null; } const tasksData = useMemo(() => { if (!data?.data?.length) { return []; } return data.data .map((stage) => ({ title: stage.title, value: stage.tasksAggregate?.aggregate?.count ?? 0, })) .filter( (stage) => stage.value !== null && stage.value !== undefined && stage.value > 0, ) .sort((a, b) => (b.value ?? 0) - (a.value ?? 0)); }, [data?.data]); return ( <div> {tasks ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai8mo ago
<@135097094825443328> kapa.ai is still learning and improving, please let us know how it did by reacting below
rival-black
rival-blackOP8mo ago
@kapa.ai With this data response: { "data": { "taskStages": [ { "title": "active", "tasksAggregate": { "aggregate": { "count": 1 } } }, { "title": "inactive", "tasksAggregate": { "aggregate": { "count": 1 } } } ] } } I get: TypeError: Cannot read properties of undefined (reading 'aggregate') at getList (@refinedev_hasura.js?v=0869cb55:12770:38) What do I need to do? Do I need to customize the data provider to handle this, or is there any other solution?
kapa.ai
kapa.ai8mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- The error you're encountering, TypeError: Cannot read properties of undefined (reading 'aggregate'), suggests that the getList function in the Hasura data provider is not correctly handling the response structure when using the graphql-default naming convention. Solution You need to customize the Hasura data provider to correctly handle the graphql-default naming convention. This involves ensuring that the aggregate operation is correctly camelized. Steps to Customize the Data Provider 1. Swizzle the Data Provider: Use the swizzle command to customize the Hasura data provider.
npm run refine swizzle

npm run refine swizzle

2. Select the Hasura Data Provider: Choose @refinedev/hasura from the list of available data providers. 3. Modify the Data Provider: Edit the /hasura-data-provider/index.ts file to customize the data provider. Specifically, you need to modify the getList function to handle the graphql-default naming convention.
import { dataProvider as baseDataProvider } from "@refinedev/hasura";
import { GraphQLClient } from "graphql-request";

const client = new GraphQLClient(API_URL, {
headers: {
"x-hasura-role": "admin",
"x-hasura-admin-secret": "xxx"
},
});

const customDataProvider = {
...baseDataProvider(client, { namingConvention: "graphql-default" }),
getList: async ({ resource, pagination, filters, sorters, meta }) => {
const { gqlQuery } = meta;
const response = await client.request(gqlQuery, {
where: filters,
orderBy: sorters,
limit: pagination.pageSize,
offset: (pagination.current - 1) * pagination.pageSize,
});
return {
data: response[resource],
total: response[resource].reduce((acc, item) => acc + (item.tasksAggregate?.aggregate?.count || 0), 0),
};
},
};

export default customDataProvider;

import { dataProvider as baseDataProvider } from "@refinedev/hasura";
import { GraphQLClient } from "graphql-request";

const client = new GraphQLClient(API_URL, {
headers: {
"x-hasura-role": "admin",
"x-hasura-admin-secret": "xxx"
},
});

const customDataProvider = {
...baseDataProvider(client, { namingConvention: "graphql-default" }),
getList: async ({ resource, pagination, filters, sorters, meta }) => {
const { gqlQuery } = meta;
const response = await client.request(gqlQuery, {
where: filters,
orderBy: sorters,
limit: pagination.pageSize,
offset: (pagination.current - 1) * pagination.pageSize,
});
return {
data: response[resource],
total: response[resource].reduce((acc, item) => acc + (item.tasksAggregate?.aggregate?.count || 0), 0),
};
},
};

export default customDataProvider;

4. Use the Customized Data Provider: Pass the customized data provider to the Refine component.
import { Refine } from "@refinedev/core";
import customDataProvider from "./hasura-data-provider";

const App = () => {
return (
<Refine
dataProvider={customDataProvider}
/* ... */
/>
);
};

export default App;

import { Refine } from "@refinedev/core";
import customDataProvider from "./hasura-data-provider";

const App = () => {
return (
<Refine
dataProvider={customDataProvider}
/* ... */
/>
);
};

export default App;

Additional Resources - Hasura Provider Example (https://refine.dev/docs/data/packages/hasura/#example) - DataProvider Tutorial (https://refine.dev/docs/tutorial/understanding-dataprovider/index/) By following these steps, you should be able to resolve the issue with the getList function and ensure that the useList hook works correctly with your Hasura GraphQL provider using the graphql-default naming convention. If you continue to experience issues, consider reviewing the linked resources for more context and potential solutions. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai8mo ago
<@135097094825443328> kapa.ai is still learning and improving, please let us know how it did by reacting below
rival-black
rival-blackOP8mo ago
@kapa.ai I already have support for the graphql-default naming convention
kapa.ai
kapa.ai8mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- Given that you already have support for the graphql-default naming convention, the issue might be related to how the useList hook is interpreting the response structure. The error TypeError: Cannot read properties of undefined (reading 'aggregate') suggests that the aggregate property is not being accessed correctly. Solution To resolve this issue, you need to ensure that the useList hook correctly interprets the response structure. Here’s how you can adjust your implementation: 1. Ensure Correct Query Structure: Make sure your GraphQL query is structured correctly to match the expected response format.
import { gql } from "@apollo/client";

export const DASHBOARD_TASKS_CHART_QUERY = gql`
query DashboardTasksChart(
$where: TaskStagesBoolExp!
$orderBy: [TaskStagesOrderBy!]
$limit: Int!
$offset: Int!
) {
taskStages(where: $where, orderBy: $orderBy, limit: $limit, offset: $offset) {
title
tasksAggregate {
aggregate {
count(columns: id)
}
}
}
}
`;

import { gql } from "@apollo/client";

export const DASHBOARD_TASKS_CHART_QUERY = gql`
query DashboardTasksChart(
$where: TaskStagesBoolExp!
$orderBy: [TaskStagesOrderBy!]
$limit: Int!
$offset: Int!
) {
taskStages(where: $where, orderBy: $orderBy, limit: $limit, offset: $offset) {
title
tasksAggregate {
aggregate {
count(columns: id)
}
}
}
}
`;

2. Use useList with Correct Type: Use the DashboardTasksChartQuery type directly in the useList hook.
import React, { useMemo } from "react";
import { useList } from "@refinedev/core";
import { gql } from "@apollo/client";
import { DashboardTasksChartQuery } from "interfaces"; // Adjust the import as necessary
export const DashboardTasksChart: React.FC = () => {
const { data, isError, error } = useList<DashboardTasksChartQuery>({
resource: "taskStages",
pagination: {
pageSize: 4,
},
filters: [
{
field: "title",
operator: "contains",
value: "",
},
],
sorters: [
{
field: "title",
order: "asc",
},
],
meta: {
gqlQuery: DASHBOARD_TASKS_CHART_QUERY
},
});

if (isError) {
console.error("Error fetching task chart data", error);
return null;
}

const tasksData = useMemo(() => {
if (!data?.data?.taskStages?.length) {
return [];
}

return data.data.taskStages
.map((stage) => ({
title: stage.title,
value: stage.tasksAggregate?.aggregate?.count ?? 0,
}))
.filter(
(stage) =>
stage.value !== null && stage.value !== undefined && stage.value > 0,
)
.sort((a, b) => (b.value ?? 0) - (a.value ?? 0));
}, [data?.data]);

return (
<div>
{tasksData.map((task) => (
<div key={task.title}>
<h3>{task.title}</h3>
<p>Count: {task.value}</p>
</div>
))}
</div>
);
};

import React, { useMemo } from "react";
import { useList } from "@refinedev/core";
import { gql } from "@apollo/client";
import { DashboardTasksChartQuery } from "interfaces"; // Adjust the import as necessary
export const DashboardTasksChart: React.FC = () => {
const { data, isError, error } = useList<DashboardTasksChartQuery>({
resource: "taskStages",
pagination: {
pageSize: 4,
},
filters: [
{
field: "title",
operator: "contains",
value: "",
},
],
sorters: [
{
field: "title",
order: "asc",
},
],
meta: {
gqlQuery: DASHBOARD_TASKS_CHART_QUERY
},
});

if (isError) {
console.error("Error fetching task chart data", error);
return null;
}

const tasksData = useMemo(() => {
if (!data?.data?.taskStages?.length) {
return [];
}

return data.data.taskStages
.map((stage) => ({
title: stage.title,
value: stage.tasksAggregate?.aggregate?.count ?? 0,
}))
.filter(
(stage) =>
stage.value !== null && stage.value !== undefined && stage.value > 0,
)
.sort((a, b) => (b.value ?? 0) - (a.value ?? 0));
}, [data?.data]);

return (
<div>
{tasksData.map((task) => (
<div key={task.title}>
<h3>{task.title}</h3>
<p>Count: {task.value}</p>
</div>
))}
</div>
);
};

Additional Resources - Hasura Provider Example (https://refine.dev/docs/data/packages/hasura/#example) - DataProvider Tutorial (https://refine.dev/docs/tutorial/understanding-dataprovider/index/) By following these steps, you should be able to resolve the issue with the getList function and ensure that the useList hook works correctly with your Hasura GraphQL provider using the graphql-default naming ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai8mo ago
<@135097094825443328> kapa.ai is still learning and improving, please let us know how it did by reacting below
rival-black
rival-blackOP8mo ago
@kapa.ai export type DashboardTasksChartQuery = { taskStages: Array< Pick<Types.TaskStages, "title"> & { tasksAggregate: { aggregate?: Types.Maybe<Pick<Types.TasksAggregateFields, "count">>; }; } >; }; Property 'taskStages' does not exist on type 'DashboardTasksChartQuery[]'. for if (!data?.data?.taskStages?.length) {. Why?
kapa.ai
kapa.ai8mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- The error Property 'taskStages' does not exist on type 'DashboardTasksChartQuery[]' suggests that the data property returned by useList is being interpreted as an array of DashboardTasksChartQuery objects, rather than a single object containing the taskStages array. Solution To resolve this issue, you need to ensure that the data property is correctly typed and accessed. Here’s how you can adjust your implementation: 1. Correct the Type Definition: Ensure that the DashboardTasksChartQuery type is correctly defined to match the expected response structure.
export type DashboardTasksChartQuery = {
taskStages: Array<
Pick<Types.TaskStages, "title"> & {
tasksAggregate: {
aggregate?: Types.Maybe<Pick<Types.TasksAggregateFields, "count">>;
};
}
>;
};

export type DashboardTasksChartQuery = {
taskStages: Array<
Pick<Types.TaskStages, "title"> & {
tasksAggregate: {
aggregate?: Types.Maybe<Pick<Types.TasksAggregateFields, "count">>;
};
}
>;
};

2. Use useList with Correct Type: Use the DashboardTasksChartQuery type directly in the useList hook and ensure that the data property is accessed correctly.
import React, { useMemo } from "react";
import { useList } from "@refinedev/core";
import { gql } from "@apollo/client";
import { DashboardTasksChartQuery } from "interfaces"; // Adjust the import as necessary

export const DASHBOARD_TASKS_CHART_QUERY = gql`
query DashboardTasksChart(
$where: TaskStagesBoolExp!
$orderBy: [TaskStagesOrderBy!]
$limit: Int!
$offset: Int!
) {
taskStages(where: $where, orderBy: $orderBy, limit: $limit, offset: $offset) {
title
tasksAggregate {
aggregate {
count(columns: id)
}
}
}
}
`;
export const DashboardTasksChart: React.FC = () => {
const { data, isError, error } = useList<DashboardTasksChartQuery>({
resource: "taskStages",
pagination: {
pageSize: 4,
},
filters: [
{
field: "title",
operator: "contains",
value: "",
},
],
sorters: [
{
field: "title",
order: "asc",
},
],
meta: {
gqlQuery: DASHBOARD_TASKS_CHART_QUERY
},
});

if (isError) {
console.error("Error fetching task chart data", error);
return null;
}

const tasksData = useMemo(() => {
if (!data?.data?.taskStages?.length) {
return [];
}

return data.data.taskStages
.map((stage) => ({
title: stage.title,
value: stage.tasksAggregate?.aggregate?.count ?? 0,
}))
.filter(
(stage) =>
stage.value !== null && stage.value !== undefined && stage.value > 0,
)
.sort((a, b) => (b.value ?? 0) - (a.value ?? 0));
}, [data?.data]);

return (
<div>
{tasksData.map((task) => (
<div key={task.title}>
<h3>{task.title}</h3>
<p>Count: {task.value}</p>
</div>
))}
</div>
);
};

import React, { useMemo } from "react";
import { useList } from "@refinedev/core";
import { gql } from "@apollo/client";
import { DashboardTasksChartQuery } from "interfaces"; // Adjust the import as necessary

export const DASHBOARD_TASKS_CHART_QUERY = gql`
query DashboardTasksChart(
$where: TaskStagesBoolExp!
$orderBy: [TaskStagesOrderBy!]
$limit: Int!
$offset: Int!
) {
taskStages(where: $where, orderBy: $orderBy, limit: $limit, offset: $offset) {
title
tasksAggregate {
aggregate {
count(columns: id)
}
}
}
}
`;
export const DashboardTasksChart: React.FC = () => {
const { data, isError, error } = useList<DashboardTasksChartQuery>({
resource: "taskStages",
pagination: {
pageSize: 4,
},
filters: [
{
field: "title",
operator: "contains",
value: "",
},
],
sorters: [
{
field: "title",
order: "asc",
},
],
meta: {
gqlQuery: DASHBOARD_TASKS_CHART_QUERY
},
});

if (isError) {
console.error("Error fetching task chart data", error);
return null;
}

const tasksData = useMemo(() => {
if (!data?.data?.taskStages?.length) {
return [];
}

return data.data.taskStages
.map((stage) => ({
title: stage.title,
value: stage.tasksAggregate?.aggregate?.count ?? 0,
}))
.filter(
(stage) =>
stage.value !== null && stage.value !== undefined && stage.value > 0,
)
.sort((a, b) => (b.value ?? 0) - (a.value ?? 0));
}, [data?.data]);

return (
<div>
{tasksData.map((task) => (
<div key={task.title}>
<h3>{task.title}</h3>
<p>Count: {task.value}</p>
</div>
))}
</div>
);
};

Additional Resources - Hasura Provider Example (https://refine.dev/docs/data/packages/hasura/#example) - [DataProvider Tutorial](https ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai8mo ago
<@135097094825443328> kapa.ai is still learning and improving, please let us know how it did by reacting below

Did you find this page helpful?