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
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:
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:
----------------------<@240371095444652032> kapa.ai is still learning and improving, please let us know how it did by reacting below
sharp-indigo•5mo 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)
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:
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:
----------------------<@240371095444652032> kapa.ai is still learning and improving, please let us know how it did by reacting below
sharp-indigo•5mo 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);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:
But in your GraphQL query, the resource name is "vehicles"
:
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"
:
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:
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:
----------------------<@240371095444652032> kapa.ai is still learning and improving, please let us know how it did by reacting below
sharp-indigo•5mo 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
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:
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:
----------------------<@240371095444652032> kapa.ai is still learning and improving, please let us know how it did by reacting below
sharp-indigo•5mo ago
@kapa.ai but the returned data structure is the same - console.log(data) returns:
data
:
nodes
:
(2) [{…}, {…}]
totalCount
:
2
[[Prototype]]
:
Object
total
:
undefined
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:
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:
----------------------<@240371095444652032> kapa.ai is still learning and improving, please let us know how it did by reacting below
sharp-indigo•5mo 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?