Add your arguments to the field
(Client Side) change from:
Car(type: $type, materialType: $materialType){
id
material
name
...
}
(Client Side) To:
Car(type: $type){
id
material(materialType: $materialType) // moved here
name
...
}
Then, access your argument in your server fieldResolver
(material
field in this case).
Longer version
Try not to pass your argument through root/info
, except IDs
and arguments that is not from client
, anything else use field level argument (unless you have a very good reason not to)
How?
Let's make it really simple:
There are only two level of argument:
- Root field argument
- Field level argument
Let's say you want to query
a customizable car (let's limit to only the seat is customizable for now)
[Root] Car(color:white, type:sedan, seat:leather)
Soon you find yourself, you need to customize the color of seat
[Root] Car(color:white, type:sedan, seat:leather, seatColor:black)
then, business grows, now we need to customize the rim as well:
[Root] Car(color:white, type:sedan, seat:leather, seatColor:black, rimShape:star,rimColor:makeitshine)
Coming next dashboard, exhaust, windows and it is never ending.
To solve this: make it to field level:
[Root] Car(color:white, type:sedan)
[Field] seat(color: black, texture:fabric)
[Field] rim(shape:star, color: makeitshine)
[Field] window(feature:bulletproof, tint:cantseeme)
......any other thing you want to add
instead of clumping all arguments into one root, now each field is responsible of its own argument and own its resolver.
Why?
There are a few reasons:
- Tight Coupling
From @Bruno Ribeiro in the comment section:
it leads to coupling and it's very hard to scale up schemas
From https://principledgraphql.com/agility#4-abstract-demand-oriented-schema
A schema focused on providing a great developer experience to an app developer building a new feature against the existing graph. Aiming for this standard will help prevent the graph from becoming coupled to a service implementation that could change in the future, and help increase the reuse value of each field added to the graph.
- Difficult to troubleshoot
One level is still okay, but when the bad habit got out of hand, and someone in your company found a way to pass the argument down few levels deep through roots, if it gone missing, it is difficult to find out where it is gone. Not fun.
- Leaking unnecessary information to children
Passing arguments through root also means passing to every other child, desired one or not.
When to apply?
Whenever you find yourself creating a dedicated resolver for that field, pass the argument to the field (not root, and worse: info)
End of the long whine.
######################
This section is to answer host question.
My question is that how can I access args which is available in root
resolver(getTotalVehicals) in any of the child resolvers?
(Server side)
type RootQuery {
getTotalVehicles(color: String): TotalVehicleResponse
}
type TotalVehicleResponse {
totalCars(color: String): Int // <-- added arguments
totalTrucks(offset: Int, limit: Int): Int // <-- added arguments
}
schema {
query: RootQuery
}
then, you can access this args in your resolver argument fields:
// In your child resolver
TotalVehicleResponse{
totalCars(parent, args, ctx){
const {color} = args // <-- access your client args here
return ....
}
totalTrucks(parent, args, ctx){
const {offset, limit} = args // <-- your args from client query
...do db query
return ....
}
}
In your client query
(Client Side)
Don't forget to add your variables in your nested query field as well.
getTotalVehicles(color: $color){
totalCars(color: $color) <-- add your variable here
totalTrucks(offset: $offset, limit: $limit) <-- add your variable here
}