Not able to consume data with two types string and object

Hello everyone,
I have created a simple Apollo graphql project and data coming from backend is in below given format, I am not able to consume below data with union, as union has condition that you can only use two objects not one scalar type. Please help -
My data model -

B
“filters”: [
{
“fieldName”: “year”,
“values”: [
“2023”
]
},
{
“fieldName”: “area-codes”,
“values”: [
{
“postalCodes”: “12345”
}
]
}
]

Blockquote
I have written graphql schema like this but not working -
type Query{
getFilters(postObj: PostObjInput!): String
}

Blockquote
type Query {
productSearch: ProductSearch
}
type ProductSearch {
filters: [Filter]
}
type FilterValue {
codes: String
}
type Filter {
fieldName: String
values: String | FilterValue
}
union StringOrFilterValue = String | FilterValue

Getting error -Union type stringorfilterValue can only inlcude object types, it cannot inlcude string

Hey @ankit9787 :wave:

This is actually a limitation of GraphQL itself. You are not able to declare unions of scalars. Object types are the only allowed union types. From the GraphQL spec:

  1. The member types of a Union type must all be Object base types; Scalar, Interface and Union types must not be member types of a Union. Similarly, wrapping types must not be member types of a Union.

Hi @jerelmiller So any workaround to implement the same, How can I receive data then

@ankit9787 could you clarify what you mean by “receive data”? Are you asking about how to query for data in a union, or are you talking about how you’d define your server GraphQL schema to allow for unions as input for a field argument?

Hi @jerelmiller I mean to say, what shall I do then, because I cannot put string and object type in union, but my data from backend is like that only, sometimes string - “abc” sometimes object- {
“key”: “value”
}

how to define a schema that can receive both type because they are coming from same variable only

@ankit9787 Unfortunately I’m still note sure exactly what you’re asking for, so I’ll answer the question I think you’re asking about, which is how to define a field in your schema that models some data that contains a property that can either be an object or string.

Something to keep in mind with GraphQL is that your schema doesn’t have to be 1:1 with the shape of your data. They typically resemble each other, but this isn’t a requirement.

In this case, you’ll need to introduce a 2nd object type in your schema for the situation where your value is a String.

I see the string value in this case looks like some kind of year. I’ll call this object a YearFilter since it looks like these values are part of a larger Filter type. That being the case, here is how you might consider creating your schema:

type Query {
  productSearch: ProductSearch
}

type ProductSearch {
  filters: [Filter]
}

# NOTE: I've renamed the old `FilterValue` to `PostalCodeFilter`. 
# Pick a suitable name if this isn't fully correct
type PostalCodeFilter {
  codes: String
}

type YearFilter {
  year: String
}

type Filter {
  fieldName: String
  values: [FilterValue]
}

# NOTE: I've renamed `FilterValue` to be the union instead
union FilterValue = PostalCodeFilter | YearFilter

You can then query for the filter values using an inline fragment:

query {
  productSearch {
    filters {
      fieldName
      values {
        ...on PostalCodeFilter {
          codes
        }
        ...on YearFilter {
          year
        }
      }
    }
  }
}

Does this help address the question you’re looking to get an answer for?

Hi @jerelmiller
No I am getting error -

“message”: “Abstract type "FilterValue" must resolve to an Object type at runtime for field "Filter.values". Either the "FilterValue" type should provide a "resolveType" function or each possible type should provide an "isTypeOf" function.”,

Let us see the data first -
“filters”: [
{
“fieldName”: “year”,
“values”: [
“2023”
]},{
“fieldName”: “area-codes”,
“values”: [
{
“postalCodes”: “12345”
}]}]

there are two types of object, one with values as string array and other has object a key value pair(postalcode : “12345”)

so when I run your query, values get confuse where to map themselves under PostalcodeFilter or yearFilter, because postalcodefilter is correct but see Yearfilter, data directly array of numbers but in your case one its values array then year variable and then string, maybe this is the mismatch, if you can understand in better manner please have a look and let me know the solution

@ankit9787 you’ll need to update your resolver functions to handle format your data in a way that conforms to the shape of your schema.

Assuming you’re using Apollo Server, you’ll need to define a __resolveType function for your union. This might look something like this (I’m unsure what the parent filterValue is actually passed in here, so you’ll need to adapt to fit your data):

const resolvers = {
  FilterValue: {
    __resolveType(filterValue) {
      if (typeof filterValue === 'string') {
        return 'YearFilter';
      }
      return 'PostalCodeFilter';
    }
  }
}

You’ll also need to make sure your YearFilter.year field resolves with the right data. The “parent” object in this case is likely the string literal, so you’ll need to return it in that resolver. That might look something like this:

const resolvers = {
  YearFilter: {
    // value is likely the string literal here. Double check the input value
    year: (value) => value
  }
}

You’ll have to play around with these a bit. I don’t have access to your full code, so I can’t give perfect advice. Best I can say right now is just to make sure your resolvers are setup correctly to handle your different data shapes. Hope that helps!

Hi @jerelmiller Nothing working, I think it is out of graphql capability to handle such data -

See one more time I am putting light to it -
our data has below format -
“filters”: [
{
“fieldName”: “year”,
“values”: [
“2023”
]},{
“fieldName”: “area-codes”,
“values”: [
{
"codes”: “12345”
}]}]

Now obivous schme will be, I am using a example type Person which has filters as one attribute
type Person{
filters: [Filters]
}
and now Filters schma will be

type Filters {
fieldname : String
values: [unionDataType]
}
and now we will describe a union which has two object lets say YearFilter and PostalFilter correct ?
type PostalCodeFilter {
codes: String
}

type YearFilter {
year: String
}

type Filter {
fieldName: String
values: [unionDataType]
}

union unionDataType= PostalCodeFilter | YearFilter

, now I get it that I write a resolver which if object comes send data to PostalCodeFilter else YearFilter but here is the catch - If you want I can push all code to github and you can try some alternatives but let me try to explain

even after resolver setup is done, if you see data, when it comes so first data is
“fieldName”: “area-codes”,
“values”: [
{
"codes”: “12345”
}
so it comes resolver see it as object and data see that codes is written in postalCodeFilter and it get mapped and we can see data now the second data comes -
“values”: [
“2023”
]
now it is direct string array, so even though resolver says goto year filter, it finds a another variable year inside, which its not expecting correct ? so it doesn’t gets map, it my second data is also of type -
“values”: [
“year” : “2023”
]
then it would have matched but it is array and year filter tries to map it to year variable and it fails.
I hope you get it this time, I am stuck from last 2 days but dont see graphql capability to create solution for it

Hey @ankit9787 :wave:

Yes, it would be super helpful if you could push something up to GitHub for me to look at. This should absolutely be possible with GraphQL, it just requires massaging your data a bit to ensure the right values are returned from your resolvers. Having some real code pushed up to GitHub can help me give you some concrete suggestions on how to make all of this work together for you. Send me a link when you get a chance and I’ll find a way to help you get this working!

Hi @jerelmiller
Here is my github code link-

Please use this project, Again let me explain requirement, I have two types of data which I have kept it example.json and want to consume it through graphql.
I have already implemented what we discussed and I am not able to achieve results - please see
image
arrays are coming as another object not in value.
tried to explain code in readme also but want to see output in below format -
“filters”: [
{
“fieldName”: “planYear”,
“values”: [
{
“value”: “2023”, “2024”
}
]
},

Hey @ankit9787 :wave:. Appreciate the GitHub link! This helps a ton.

I think I’m finally understanding your ask here. You want the response from your GraphQL server to match the structure from that example.json, where the values property is an array of plain strings vs an array of objects with a value property. Does this sound accurate? Apologies if I’ve been misunderstanding your ask up to this point.


Unfortunately I don’t think you’re going to be able to get the result your looking for, at least if you need to distinguish between the 2 different filter types. GraphQL doesn’t allow unions on scalar types, which is unfortunately what you’d need here to be able to consume the data in the format you’re hoping for.

If you want to consume the data with plain strings in the array, all of the filter values are going to need to be plain strings:

type Filter {
  fieldName: String
  values: [String]
}

This has the downside however that you lose the filter type in the response and you’ll have to try and determine at runtime whether the string is a year or postal code. The union solution is a very straightforward way to handle this since you can use __typename to determine what type of filter the value is.


All this said, I’m curious why you’re set on trying to consume one of the two values as a plain string. The consumer of the GraphQL query should be able to do its own post processing and manipulate that array to be an array of strings if need be. I’m curious what you’re trying to solve for. I’d challenge the fact that you lose type information on one of the 2 filter types if your server was able to return plain strings for one of the two types. What are you looking to solve that a union of objects doesn’t get you?