atulmy/gql-query-builder

support for union types

Closed this issue ยท 10 comments

is there support for unions and/or interfaces? one of my api calls returns content and content is a union type that can return diff types of models.

Example desired query output:

{
  vehicle {
     ... on Car {
       name
       isSedan
       NumSeats
       topSpeed
     }

     ... on Scooter {
       name
       isElectric
       numMiles
       topSpeed
    }
  }
}

@bebraw / @toadkicker any ideas on Unions / Interfaces?

#11 will fix this =)

@toadkicker Can you give an example here for @albertalquisola use case?

https://github.com/NowServing/gql-query-builder/blob/appsync_support/src/adapters/DefaultQueryAdapter.ts#L112

You can basically attack this however you want by creating your own adapter. I'm guessing this is a conditional thing you want, and so one approach could change the adapter to look for a unions flag in the querybuilder options.

Thanks for being responsive @atulmy and @toadkicker. For my 2 cents, i'd love for the api to be something like this:

query({
  operation: 'vehicles',
  possibleTypes: [
    { name: 'Car', fields: [{ owner: ['firstName', 'lastName' ]}, 'name', 'isSedan', 'numSeats', 'topSpeed'] },
    { name: 'Scooter', fields: ['name', 'isElectric', 'numMiles', 'topSpeed'] },
  ],
});

// output
`
  {
    vehicles {
      ... on Car {
        owner { 
          firstName
          lastName
        }
        name
        isSedan
        NumSeats
        topSpeed
      }
    
      ... on Scooter {
        name
        isElectric
        numMiles
        topSpeed
      }
    }
  }
`

I'll also look over the adapter pr as well and see how flexible it is

The idea is that the adapter maintains the same documented API that the query builder adheres to. A custom adapter is just a Javascript class that implements the query/mutation adapter interface. Another way to say it is the adapter is the engine of the output, and for the engine to work it needs to have the right inputs.

In @albertalquisola particular use case consider ...car in your fields., and then creating a custom adapter (it can extend from the default one) that outputs unions based on ... provided in there. Or even simpler don't worry about detecting it, just hard code it to use the custom adapter when called.

@toadkicker, I get the concept behind the interface adapter, but could you post a pseudocode example?

Can just make UnionAdapter, and change from the default:

private operationTemplate() {
    return `... on ${this.operation.charAt(0).toUpperCase() + this.operation.substring(1)} { ${Utils.queryFieldsMap(
      this.fields
    )} }`;
}

@albertalquisola let us know if the adapter example was of help

Not too pretty but it does the trick:

  • Create a custom adapter for the type of operation you want (based on DefaultQueryAdapter in my case)
  • Replace the imports accordingly:
// UnionQueryAdapter.ts
import Fields from 'gql-query-builder/build/Fields';
import IQueryBuilderOptions from 'gql-query-builder/build/IQueryBuilderOptions';
import OperationType from 'gql-query-builder/build/OperationType';
import Utils from 'gql-query-builder/build/Utils';
import IQueryAdapter from 'gql-query-builder/build/adapters/IQueryAdapter';
import VariableOptions from 'gql-query-builder/build/VariableOptions';
  • Replace the operationTemplate method with the following:
// UnionQueryAdapter.ts
private operationTemplate(variables: VariableOptions | undefined) {
  return `${this.operation} ${
    variables ? Utils.queryDataNameAndArgumentMap(variables) : ''
  } ${
    this.fields && this.fields.length > 0
      ? `{ ${this.fields
          .map((query: any) => `... on ${Object.keys(query)[0]} { ${
            Utils.queryFieldsMap(query[Object.keys(query)[0]])
          } }`)
          .join(' ')} }`
      : ''
  }`;
}
  • Call your union like this:
query({
    operation: 'foobar',
    fields: [{ Foo: ['foofoo'] }, { Bar: ['barbar'] }],
  },
  UnionQueryAdapter,
)

// Output
query  { 
  views  { 
    ... on Foo { 
      foofoo 
    } 
    ... on Bar { 
      barbar 
    } 
  } 
}

Hope this helps some people that were struggling with unions like myself.
Note: this solution isn't 100% complete but it should help kickstart your union needs ๐Ÿ˜„
Edit: here's the full adapter file: UnionQueryAdapter.ts