Is it a bad practice to use an Input Type for a graphql Query? - graphql

I have seen that inserting an Input Type is recommended in the context of mutations but does not say anything about queries.
For instance, in learn tutorial just say:
This is particularly valuable in the case of mutations, where you might want to pass in a whole object to be created
I have this query:
type query {
person(personID: ID!): Person
brazilianPerson(rg: ID!): BrazilizanPerson
foreignerPerson(passport: ID!): ForeignerPerson
}
Instead of having a different type just because of the name (rg, passport) of the fields, or put one more argument like type in query, I could not just have the Person with an documentNr field and do an Input type like that?
input PersonInput {
documentNr : ID!
type: PersonType # this type is Foreign or Brazilian and with this I k
}
PersonType is a enum and with him I know if the document is a rg or a passport.

No, there is nothing incorrect about your approach. The GraphQL spec allows any field to have an argument and allows any argument to accept an Input Object Type, regardless of the operation. In fact, the differences between a query and a mutation are largely symbolic.
It's worth pointing out that any field can accept an argument -- not just ones at the root level. So if it suited your needs, you could easily set up a schema that would allow queries like:
query {
person(id: 1) {
powers(onlyMutant: true) {
name
}
}
}

Related

Can one combine two types to make a third in GraphQL schema syntax?

I have a feeling this will be deemed Not How You Do It In GraphQL, but I'm pretty new to it, so please be patient and verbose with me.
Let's say I've got two GraphQL types that I'd like to be able to utilize separately:
type UserSpecs {
name: String!
age: Int!
bio: String!
}
type UserCollections {
interests: [Interest]
buddies: [Relationship]
chats: [Chat]
}
type Query {
updateCollections(collections: UserCollections): User
updateUserSpecs(specs: UserSpecs): User
}
In my .gql file, I'd like to also define the User type as the combination of UserSpecs and UserCollections, though. In TypeScript, for instance, one would do this:
type User = UserSpecs & UserCollections
Short of manually duplicating the contents of UserSpecs and UserCollections into a third type, which would not be DRY and would create two sources of truth to maintain, does the GraphQL schema syntax have any way of combining two types to make a third?
Similarly, if it's possible to create a User type, then disassemble it into the UserSpecs and UserCollections types I'm after, that would be equally helpful.
Thank you in advance!

I need a type for a graphql property that could be two 2 types

So I have some code like the following:
input Data {
activityValue: Int
}
But I need it to be something more like
input Data {
activityValue: Int | String!
}
I know in typescript, even though frowned upon you can use any or number | string. Is there anything like this in graphql?
There is no real such thing as multiple types in the GraphQL specification. However, Unions can fit your needs.
From the specification:
GraphQL Unions represent an object that could be one of a list of GraphQL Object types, but provides for no guaranteed fields between those types.
That means that Unions can include types but no scalars or lists.
For example, a union can be declared like this:
union Media = Book | Movie
And then be used as a type:
type Query {
allMedia: [Media] # This list can include both Book and Movie objects
}
Example is taken from Apollo Docs.
If you want to check in your query if you have some type of the Union type, then you need to do that with inline fragments.
query Test {
singleMedia(id: 123) {
name
... on Book {
author
}
... on Movie {
musicTitle
}
}
}

GraphQL mutation with subquery for parameter - update/convert DB on write?

Is it possible to add a look up to a mutation in GraphQL? Let's say something like an input type where one property is the result of another query.
createPerson(name: "Steve", gender: genders ( name: { eq: "mail" } ) { id } )
where genders ( name: { eq: "mail" } ) { id } would return exactly one result value
GraphQL allows you to:
"get what you want";
manipulate data on write;
customize response on read;
You can of course write a createPerson mutation resolver to:
ask another table for additional data (if missing arg);
write into DB;
But it doesn't affect (isn't suitable for) existing records.
You can also use read resolvers to update missing fields in records using field resolver - a kind of 'eventual consistency'.
Write person resolver to read a person record and additional person.gender field resolver to get value for missing gender value. The second one can be used to update the 'main' person [DB table] record:
read a missing value from other table;
write a value into person record on the 'main' table;
return value.
Next time person resolver will read gender value at once (from 'main' person table), [additional] field resolver won't be called at all.
This technique can be used to convert DB data without writing SQL/DB specific code (different syntax/types problems, safer on more complex logic) - it's enough to read each/all person's gender fields. Field resolver (and the other/old table) can be removed after that.

Understanding GraphQL Union types

While experimenting with the Union types in GraphQL here: https://graphql.github.io/learn/schema/#union-types I ran into the following:
I initially thought that the fields you specify in the query are the fields that going to be searched for the text: "Millenium", however that's not the case because I'm still getting the Millenium Falcon's data even after removing the name field from the query for the Startship type.
I did another test: R2-D2's primaryFunction is Astromech, if you search for Astromech you'll get nothing, even if primaryFunction is specified for Droid type.
Note that name is still specified on Starship because otherwise it wouldn't show up in the results given that Starship is not a Character!
That simply means that because we are using a Union type, given two types that are part of the Union that both have a name field, you still have to request the name field for each type in their inline fragment. Omitting the name for the Starship fragment, but including it on Character, means if the returned type is a Character the name field will be present but it will not be present on the Starship type.
The docs are mentioning this to highlight the difference between Unions and Interfaces. If SearchResult was an Interface that included the name field and Character and Starship implemented that Interface, you could do something like this instead:
{
search(text: "an") {
name
__typename
... on Human {
height
}
... on Droid {
primaryFunction
}
... on Starship {
length
}
}
}
But because Unions don't guarantee any fields are shared between their types, it's not possible to do so with Unions.
With regards to search, that's not something that's baked into GraphQL. This particular schema happens to have a search field on its Query type and that field resolves a particular way. If you were creating a server, you could write a search field that considered the requested fields as part of the search criteria. But this is an implementation detail and not related to how GraphQL works in general.

Using GraphQL with conditional related types

I have an app that has a type with many related types. So like:
type Person {
Name: String!
Address: Address!
Family: [Person!]!
Friends: [Person!]!
Job: Occupation
Car: Car
}
type Address {...}
type Occupation {...}
type Car {...}
(don't worry about the types specifically...)
Anyway, this is all stored in a database in many tables.
Some of these queries are seldom used and are slow. Imagine for example there are billions of cars in the world and it takes time to find the one that is owned by the person we are interested in. Any query to "getPerson" must satisfy the full schema and then graphql will pare it down to the fields that are needed. But since that one is slow and could be requested, we have to perform the query even though the data is thrown out most of the time.
I only see 2 solutions to this.
a) Just do the query each time and it will always be slow
b) Make 2 separate Query options. One for "getPerson" and one "getPersonWithCar" but then you're not able to reuse the schema and now a Person is defined twice. Once in terms of the car and once without.
Is there a way to indicate whether a field is present in the Query requested fields? That way we could say like
if (query.isPresent("Car")) {
car = findCar();
} else {
car = null;
}

Resources