sharp-indigo
sharp-indigo5mo ago

How to define GraphQL Schema?

Hey I am running a backend django + strawberry and refine with graphql to make some calls and read data. I keep getting TypeError: Cannot read properties of undefined (reading 'count') When trying to run a simple: const { data, isLoading, error } = useList({ resource: "vehicle_list", meta: { gqlQuery: VEHICLES_LIST_QUERY }, }); with this query: export const VEHICLES_LIST_QUERY = gql query VehiclesList { vehicles { id vin make model year driver { id name email licenseNumber } gateway { id serialNumber firmwareVersion } } } ; Is there a specific Graphql query response format that suppose to work? Seems like it is expecting data in a certain format.
164 Replies
kapa.ai
kapa.ai5mo ago
Hi @Valugar. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Yes, Refine expects the GraphQL response in a certain format. For the useList hook, the response should include a totalCount field that indicates the total number of records. This is used for pagination purposes. Here is an example of how the response should look like:
{
"data": {
"resource": {
"nodes": [
{
"id": "1",
"title": "Title 1"
},
{
"id": "2",
"title": "Title 2"
}
],
"totalCount": 2
}
}
}
{
"data": {
"resource": {
"nodes": [
{
"id": "1",
"title": "Title 1"
},
{
"id": "2",
"title": "Title 2"
}
],
"totalCount": 2
}
}
}
In this example, resource should be replaced with the name of your resource, which is vehicles in your case. The nodes field should contain an array of your resource items. Each item should at least contain an id field. If your GraphQL server does not return the data in this format, you might need to create a custom data provider to adapt the server response to the format that Refine expects. You can refer to the Refine's GraphQL documentation (https://refine.dev/docs/packages/documentation/data-providers/graphql/) for more information about its GraphQL implementation. Also, please note that the error message TypeError: Cannot read properties of undefined (reading 'count') indicates that the useList hook is trying to read the count property of an undefined object. This might be caused by the server returning null or undefined for the totalCount field or the entire resource. Please make sure that your server returns a valid object for the resource and a valid number for the totalCount field. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai5mo ago
<@240371095444652032> kapa.ai is still learning and improving, please let us know how it did by reacting below
sharp-indigo
sharp-indigo5mo ago
@kapa.ai so given my schema, how should I change it to fit the expected schema? @strawberry.type class VehicleConnection: nodes: List[VehicleType] totalCount: int @strawberry.type class Query: @strawberry.field async def vehicles(self, info: strawberry.Info, where: Optional[JSON] = None, sort: Optional[str] = None, start: Optional[int] = None, limit: Optional[int] = None) -> VehicleConnection: query = Vehicle.objects.all() Apply filtering if where: query = query.filter(**where) Apply sorting if sort: query = query.order_by(sort) # Apply pagination if start is not None and limit is not None: query = query[start:start + limit] totalCount = await sync_to_async(query.count)() vehicle_list = await sync_to_async(list)(query) return VehicleConnection(nodes=vehicle_list, totalCount=totalCount)
kapa.ai
kapa.ai5mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- Your schema seems to be correctly defined for use with Refine. The VehicleConnection class has the nodes and totalCount fields, which are required by Refine. The vehicles field in the Query class returns a VehicleConnection, which is also correct. The nodes field should contain an array of VehicleType objects, and each VehicleType object should at least contain an id field. The totalCount field should contain the total number of VehicleType objects. Here is an example of how the response should look like:
{
"data": {
"vehicles": {
"nodes": [
{
"id": "1",
"vin": "VIN1",
"make": "Make1",
"model": "Model1",
"year": "Year1",
"driver": {
"id": "1",
"name": "Name1",
"email": "Email1",
"licenseNumber": "LicenseNumber1"
},
"gateway": {
"id": "1",
"serialNumber": "SerialNumber1",
"firmwareVersion": "FirmwareVersion1"
}
},
{
"id": "2",
"vin": "VIN2",
"make": "Make2",
"model": "Model2",
"year": "Year2",
"driver": {
"id": "2",
"name": "Name2",
"email": "Email2",
"licenseNumber": "LicenseNumber2"
},
"gateway": {
"id": "2",
"serialNumber": "SerialNumber2",
"firmwareVersion": "FirmwareVersion2"
}
}
],
"totalCount": 2
}
}
}
{
"data": {
"vehicles": {
"nodes": [
{
"id": "1",
"vin": "VIN1",
"make": "Make1",
"model": "Model1",
"year": "Year1",
"driver": {
"id": "1",
"name": "Name1",
"email": "Email1",
"licenseNumber": "LicenseNumber1"
},
"gateway": {
"id": "1",
"serialNumber": "SerialNumber1",
"firmwareVersion": "FirmwareVersion1"
}
},
{
"id": "2",
"vin": "VIN2",
"make": "Make2",
"model": "Model2",
"year": "Year2",
"driver": {
"id": "2",
"name": "Name2",
"email": "Email2",
"licenseNumber": "LicenseNumber2"
},
"gateway": {
"id": "2",
"serialNumber": "SerialNumber2",
"firmwareVersion": "FirmwareVersion2"
}
}
],
"totalCount": 2
}
}
}
If you are still encountering the error, it might be due to the server returning null or undefined for the totalCount field or the entire vehicles object. Please make sure that your server returns a valid object for vehicles and a valid number for totalCount. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai5mo ago
<@240371095444652032> kapa.ai is still learning and improving, please let us know how it did by reacting below
sharp-indigo
sharp-indigo5mo ago
@kapa.ai But this is exactly the query response I get from my schema. So it is proper. Is there something maybe that is badly configured with the data provider? const { data, isLoading, isError } = useList({ resource: "vehicles_list", meta: { gqlQuery: VEHICLES_LIST_QUERY }, }); const client = new GraphQLClient(API_URL, { fetch: (url: string, options: RequestInit) => { const token = localStorage.getItem("token"); // Ensure you get the token from a secure place return fetch(url, { ...options, headers: { ...options.headers, Authorization: Bearer ${token}, }, }); }, }); const gqlDataProvider = dataProvider(client);
kapa.ai
kapa.ai5mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- Your GraphQL data provider setup seems correct. You're creating a new GraphQLClient with your API URL and a fetch function that includes the authorization token in the headers. Then, you're passing this client to the dataProvider function from Refine to create the data provider. However, the issue might be related to the resource name you're using in the useList hook. In your hook, you're using "vehicles_list" as the resource name:
const { data, isLoading, isError } = useList({
resource: "vehicles_list",
meta: { gqlQuery: VEHICLES_LIST_QUERY },
});
const { data, isLoading, isError } = useList({
resource: "vehicles_list",
meta: { gqlQuery: VEHICLES_LIST_QUERY },
});
But in your GraphQL query, the resource name is "vehicles":
query VehiclesList {
vehicles {
id
vin
make
model
year
driver {
id
name
email
licenseNumber
}
gateway {
id
serialNumber
firmwareVersion
}
}
}
query VehiclesList {
vehicles {
id
vin
make
model
year
driver {
id
name
email
licenseNumber
}
gateway {
id
serialNumber
firmwareVersion
}
}
}
The resource name in the useList hook should match the resource name in your GraphQL query. Try changing the resource name in the useList hook to "vehicles":
const { data, isLoading, isError } = useList({
resource: "vehicles",
meta: { gqlQuery: VEHICLES_LIST_QUERY },
});
const { data, isLoading, isError } = useList({
resource: "vehicles",
meta: { gqlQuery: VEHICLES_LIST_QUERY },
});
If you're still encountering the error, it might be helpful to log the data and isError variables to see what data you're receiving and whether there are any errors:
console.log(data);
console.log(isError);
console.log(data);
console.log(isError);
This might give you more information about what's going wrong. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai5mo ago
<@240371095444652032> kapa.ai is still learning and improving, please let us know how it did by reacting below
sharp-indigo
sharp-indigo5mo ago
@kapa.ai Ok you are right. Now I have this: export const VehicleListPage = () => { const { data, isLoading, isError } = useList({ resource: "vehicles", meta: { gqlQuery: VEHICLES_LIST_QUERY }, }); if (isLoading) { return <div>Loading...</div>; } if (isError) { return <div>Something went wrong!</div>; } console.log(data.data.nodes) But the data.data.nodes seems to give a warning Property 'nodes' does not exist on type 'BaseRecord[]'.ts(2339) Even though it is working
kapa.ai
kapa.ai5mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- The warning you're seeing is a TypeScript type checking warning. It's saying that the nodes property does not exist on the type BaseRecord[]. This is because the data returned by useList is typed as BaseRecord[], which does not have a nodes property. To fix this, you can define a type for your vehicle data and use it in the useList hook. Here's an example:
interface IVehicle {
id: string;
vin: string;
make: string;
model: string;
year: string;
driver: {
id: string;
name: string;
email: string;
licenseNumber: string;
};
gateway: {
id: string;
serialNumber: string;
firmwareVersion: string;
};
}

interface IVehicleData {
nodes: IVehicle[];
}

const { data, isLoading, isError } = useList<IVehicleData>({
resource: "vehicles",
meta: { gqlQuery: VEHICLES_LIST_QUERY },
});

console.log(data?.nodes);
interface IVehicle {
id: string;
vin: string;
make: string;
model: string;
year: string;
driver: {
id: string;
name: string;
email: string;
licenseNumber: string;
};
gateway: {
id: string;
serialNumber: string;
firmwareVersion: string;
};
}

interface IVehicleData {
nodes: IVehicle[];
}

const { data, isLoading, isError } = useList<IVehicleData>({
resource: "vehicles",
meta: { gqlQuery: VEHICLES_LIST_QUERY },
});

console.log(data?.nodes);
In this example, IVehicle is the type for a vehicle, and IVehicleData is the type for the data returned by the useList hook. The useList hook is parameterized with IVehicleData, which tells TypeScript that data is of type IVehicleData. This way, TypeScript knows that data has a nodes property. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai5mo ago
<@240371095444652032> kapa.ai is still learning and improving, please let us know how it did by reacting below
sharp-indigo
sharp-indigo5mo ago
@kapa.ai but the returned data structure is the same - console.log(data) returns: data : nodes : (2) [{…}, {…}] totalCount : 2 [[Prototype]] : Object total : undefined
kapa.ai
kapa.ai5mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- The returned data structure is indeed the same, but TypeScript is a statically typed language, which means it checks the types of variables at compile time. Even though the actual data returned at runtime has a nodes property, TypeScript doesn't know this at compile time because the BaseRecord[] type doesn't have a nodes property. To fix the TypeScript warning, you can define a type for the returned data and use it in the useList hook. Here's an example:
interface IVehicle {
id: string;
vin: string;
make: string;
model: string;
year: string;
driver: {
id: string;
name: string;
email: string;
licenseNumber: string;
};
gateway: {
id: string;
serialNumber: string;
firmwareVersion: string;
};
}

interface IVehicleData {
nodes: IVehicle[];
totalCount: number;
}

const { data, isLoading, isError } = useList<IVehicleData>({
resource: "vehicles",
meta: { gqlQuery: VEHICLES_LIST_QUERY },
});

console.log(data?.nodes);
interface IVehicle {
id: string;
vin: string;
make: string;
model: string;
year: string;
driver: {
id: string;
name: string;
email: string;
licenseNumber: string;
};
gateway: {
id: string;
serialNumber: string;
firmwareVersion: string;
};
}

interface IVehicleData {
nodes: IVehicle[];
totalCount: number;
}

const { data, isLoading, isError } = useList<IVehicleData>({
resource: "vehicles",
meta: { gqlQuery: VEHICLES_LIST_QUERY },
});

console.log(data?.nodes);
In this example, IVehicle is the type for a vehicle, and IVehicleData is the type for the data returned by the useList hook. The useList hook is parameterized with IVehicleData, which tells TypeScript that data is of type IVehicleData. This way, TypeScript knows that data has a nodes property and a totalCount property. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai5mo ago
<@240371095444652032> kapa.ai is still learning and improving, please let us know how it did by reacting below
sharp-indigo
sharp-indigo5mo ago
@kapa.ai why is this keep returning the data as data : {totalCount: 2, nodes: Array(2)} total : undefined instead of IVehicleData? I changed it to fit what you did. Could it be the resource issue?