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

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
}
}
}

Related

Perform graphQL query with result from another graphQL query [duplicate]

Hullo everyone,
This has been discussed a bit before, but it's one of those things where there is so much scattered discussion resulting in various proposed "hacks" that I'm having a hard time determining what I should do.
I would like to use the result of a query as an argument for another nested query.
query {
allStudents {
nodes {
courseAssessmentInfoByCourse(courseId: "2b0df865-d7c6-4c96-9f10-992cd409dedb") {
weightedMarkAverage
// getting result for specific course is easy enough
}
coursesByStudentCourseStudentIdAndCourseId {
nodes {
name
// would like to be able to do something like this
// to get a list of all the courses and their respective
// assessment infos
assessmentInfoByStudentId (studentId: student_node.studentId) {
weightedMarkAverage
}
}
}
}
}
}
Is there a way of doing this that is considered to be best practice?
Is there a standard way to do it built into GraphQL now?
Thanks for any help!
The only means to substitute values in a GraphQL document is through variables, and these must be declared in your operation definition and then included alongside your document as part of your request. There is no inherent way to reference previously resolved values within the same document.
If you get to a point where you think you need this functionality, it's generally a symptom of poor schema design in the first place. What follows are some suggestions for improving your schema, assuming you have control over that.
For example, minimally, you could eliminate the studentId argument on assessmentInfoByStudentId altogether. coursesByStudentCourseStudentIdAndCourseId is a field on the student node, so its resolver can already access the student's id. It can pass this information down to each course node, which can then be used by assessmentInfoByStudentId.
That said, you're probably better off totally rethinking how you've got your connections set up. I don't know what your underlying storage layer looks like, or the shape your client needs the data to be in, so it's hard to make any specific recommendations. However, for the sake of example, let's assume we have three types -- Course, Student and AssessmentInfo. A Course has many Students, a Student has many Courses, and an AssessmentInfo has a single Student and a single Course.
We might expose all three entities as root level queries:
query {
allStudents {
# fields
}
allCourses {
# fields
}
allAssessmentInfos {
# fields
}
}
Each node could have a connection to the other two types:
query {
allStudents {
courses {
edges {
node {
id
}
}
}
assessmentInfos {
edges {
node {
id
}
}
}
}
}
If we want to fetch all students, and for each student know what courses s/he is taking and his/her weighted mark average for that course, we can then write a query like:
query {
allStudents {
assessmentInfos {
edges {
node {
id
course {
id
name
}
}
}
}
}
}
Again, this exact schema might not work for your specific use case but it should give you an idea around how you can approach your problem from a different angle. A couple more tips when designing a schema:
Add filter arguments on connection fields, instead of creating separate fields for each scenario you need to cover. A single courses field on a Student type can have a variety of arguments like semester, campus or isPassing -- this is cleaner and more flexible than creating different fields like coursesBySemester, coursesByCampus, etc.
If you're dealing with aggregate values like average, min, max, etc. it might make sense to expose those values as fields on each connection type, in the same way a count field is sometimes available alongside the nodes field. There's a (proposal)[https://github.com/prisma/prisma/issues/1312] for Prisma that illustrates one fairly neat way to do handle these aggregate values. Doing something like this would mean if you already have, for example, an Assessment type, a connection field might be sufficient to expose aggregate data about that type (like grade averages) without needing to expose a separate AssessmentInfo type.
Filtering is relatively straightforward, grouping is a bit tougher. If you do find that you need the nodes of a connection grouped by a particular field, again this may be best done by exposing an additional field on the connection itself, (like Gatsby does it)[https://www.gatsbyjs.org/docs/graphql-reference/#group].

graphql filter based on internal fragments (gatsbyJS)

Why is this not possible? In the sense that it looks like I have no access to any property accessed through an internal fragment such as ... on File
codebox from gatsby-docs
{
books: allMarkdownRemark(filter: {parent: {sourceInstanceName: {eq: "whatever"}}}) {
totalCount
edges {
node {
parent {
... on File {
sourceInstanceName
}
}
}
}
}
}
Error: Field is not defined by type NodeFilterInput
It's a resolver authors responsibility.
You can compare it to general function arguments and returned result. In graphQL both are strictly defined/typed.
In this case, for query allMarkdownRemark you have
allMarkdownRemark(
filter: MarkdownRemarkFilterInput
limit: Int
skip: Int
sort: MarkdownRemarkSortInput
): MarkdownRemarkConnection!
... so possible arguments are only filter, limit, skip and sort. Argument filter has defined shape, too - it has to be MarkdownRemarkFilterInput type. You can only use properties defined in this type for filter argument.
This is by design, this is how designer created resolver and his intentions about how and what arguments are handled.
It's like pagination - you don't have to use any of result fields as arguments as skip and limit are for record level. This way those arguments are not related to fields at all. They are used for some logic in resolver. filter argument is used for logic, too ... but it's deleveloper decision to choose and cover filtering use cases.
It's impossible to cover all imaginable filters on all processed data layers and properties, ... for parent you can only use children, id, internal and parent properties and subproperties (you can explore them in playground).
Of course it's not enough to extend type definition to make it working with another argument - it's about code to handle it.
If you need onother filtering logic, you can write your own resolver (or modify forked gatsby project) for your file types or other source.

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

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
}
}
}

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.

Serialize to JSON dynamic structure

All examples of working with JSON describe how to serialize to JSON simple or user types (like a struct).
But I have a different case: a) I don't know the fields of my type/object b) every object will have different types.
Here is my case in pseudocode:
while `select * from item` do
while `select fieldname, fieldvalue from fields where fields.itemid = item.id` do
...
For each entity in my database I get field names and field values. In the result I need to get something like this:
{
"item.field1": value,
...
"item.fieldN": value,
"custom_fields": {
"fields.field1": value,
...
"fields.fieldK": value
}
}
What is the best way to do it in Go? Is there any useful libraries or functions in standard library ?
Update: The source of data is the database. In the result i need to get JSON as string to POST it to external web service. So, the program just read data from database and make POST requests to REST service.
What exactly is your target type supposed to be? It can't be a struct since you do not know the fields beforehand.
The only fitting type to me seems to be a map of type map[string]interface{}: with it any nested structure can be achieved:
a := map[string]interface{}{
"item.field1": "val1",
"item.field2": "val2",
"item.fieldN": "valN",
"custom_fields": map[string]interface{}{
"fields.field1": "cval1",
"fields.field2": "cval2",
},
}
b, err := json.Marshal(a)
See playground sample here.
Filling this structure from a database as you hinted at should probably be a custom script (not using json).
Note: custom_fields can also be of other types depending on what type the value column is in the database. If the value column is a string use map[string]string.

Resources