Reputation: 6529
I'm wondering how to use both an input type and one of its fields as arguments in the same GraphQL query. I think there are a few valid solutions, but I'm wondering which (if any) are best practice.
Consider the following hypothetical query. We get players by location and status, and team members who are in the same location, but the member
field only has a location
argument:
input PlayerInput {
location: String!
status: Int!
}
query getPlayers($playerInput: PlayerInput) {
players(playerInput: $playerInput) {
name
team {
name
members(location: ???) { // <-- How to access playerInput.location?
name
}
}
}
}
I can think of a few ways to solve this:
1. Change the query to take individual arguments
query getPlayers($location: String!, $status: Int!) {
players(playerInput: { location: $location, status: $status }) {
name
team {
name
members(location: $location) {
name
}
}
}
}
2. Update the schema so members
takes the right input type
query getPlayers($playerInput: PlayerInput) {
players(playerInput: $playerInput) {
name
team {
name
members(playerInput: $playerInput) { // <-- Requires changing schema
name
}
}
}
}
This doesn't seem great for a few reasons, and would only work if you have the ability to update the schema.
3. Pass location
in as a redundant individual argument
query getPlayers($playerInput: PlayerInput, $location: String!) {
players(playerInput: $playerInput) {
name
team {
name
members(location: $location) {
name
}
}
}
}
This seems fine, just that there's some duplication when creating the query:
const location = 'US';
const status = 1;
fetch({
query: getPlayersQuery,
variables: {
location,
playerInput: {
location,
status,
}
}
})...
Are any of these the preferred way of doing something like this? Are there other ways I haven't considered?
Upvotes: 7
Views: 2878
Reputation: 84677
Utilizing multiple arguments over a single argument that takes an input type (option #1) sometimes makes sense conceptually, but it lacks any other real merit and has the downside of making your variable definitions unnecessarily verbose. In my opinion, using input types typically enforces typings better as well. Changing your schema just to avoid duplication is not worth it.
Utilizing the same input object type on both fields (option #2) should only be done if both fields actually need all those input fields. Doing it just to avoid duplication is a bad idea -- it effectively introduces inputs that are unused, which is not a good experience for any other developer consuming the API.
Option #3 is fine. It also puts the ball in the front end dev's court -- if they wanted to avoid the duplication, they could also just as easily do this without you introducing additional arguments:
query getPlayers($status: Int!, $location: String!) {
players(playerInput: { status: $status, location: $location }) {
name
team {
name
members(location: $location) {
name
}
}
}
}
However, the duplication is most likely actually a good thing. You don't encounter these scenarios too much in the wild because we're dealing with graphs of data. The players
, team
and members
fields are all part of a hierarchy, where the parent field constrains the data returned by the child -- for example, members
doesn't return all members, just members of that particular team. That means, in most cases if you constrain players
to only include data from a particular location, you're also constraining the data returned by child fields like members
. If you feel like members
needs its own location
argument, then that implies that the two location inputs could be different. If they could be different, they should be represented as two separate variables to maximize flexibility and query reuse.
Upvotes: 5