Search code examples
graphqlgraphql-jsexpress-graphql

Write dynamic schema for return same result in Graphql


Currently this schema working properly and give required result

  type Personal{
    userId:String
    name: String
    email: String
  }
  type Contact{
    mobile_no:String
    email:String
  }
  type Address{
    address:String
    lat:String
    long:String
  }
  getContact(userId: String): Contact
  getPersonal(userId: String): Personal
  getAddress(userId: String): Address

But I want to return

 type Response {
    status: Boolean
    message: String
    data: []
  }

Return data with status and message key where data hold an array of Contact, Personal and Address objects.

without writing ResponseContact, ResponsePersonal and ResponseAddress

I have Idea to return scalar JSON in data like this

 scalar JSON
 type Response {
   status: Boolean
   message: String
   data: [JSON]
 }

But the problem with this schema I can't use graphql second main point "Ask for what you want" Required result

type ResponseAddress {
    status: Boolean
    message: String
    data: [Address]
}
type ResponsePersonal {
    status: Boolean
    message: String
    data: [Personal]
}
type ResponseContact {
    status: Boolean
    message: String
    data: [Contact]
}
getContact(userId: String): ResponseContact
getPersonal(userId: String): ResponsePersonal
getAddress(userId: String): ResponseAddress

without writing ResponseAddress, ResponsePersonal and ResponseContact.

Something like that

type Response {
    status: Boolean
    message: String
    data: [Address|Personal|Contact]
}
getContact(userId: String): Response
getPersonal(userId: String): Response
getAddress(userId: String): Response

Of course above syntax are wrong.

Why:- Because I want to return this Response more places and don't want to long schema.

Point:- Is this possible or not?


Solution

  • Assigning multiple types to a single field is possible by using either an interface or an union:

    union ResponseData = Address | Personal | Contact
    
    type Response {
        status: Boolean
        message: String
        data: ResponseData
    }
    

    Keep in mind, when querying this field, the client will need to utilize inline fragments in order to specify which fields to request for each possible type:

    query {
      getContact {
        status
        message
        data {
          ... on Address {
            # Address field here
          }
          ... on Personal {
            # Personal field here
          }
          ... on Contact {
            # Contact field here
          }
        }
      }
    }
    

    You can check out the docs for a detailed explanation of how to implement both unions and interfaces. Keep in mind that you can only create a union of object types, so if you need a response that returns a list, you'll need to define at least two types for your responses:

    type Response {
      status: Boolean
      message: String
      data: ResponseData
    }
    
    type ListResponse {
      status: Boolean
      message: String
      data: [ResponseData]
    }
    

    Note: Using unions this way does add some complexity on the client-side and I would say generally that's not worth it just to have a smaller schema. Tools like GraphiQL and GraphQL Playground make working with large schemas a breeze for the consumer. Having a large schema with redundant types is not a bad thing if that's what you need.