[GH-ISSUE #13] support for union types #102

Closed
opened 2026-03-15 11:29:31 +03:00 by kerem · 10 comments
Owner

Originally created by @albertalquisola on GitHub (May 28, 2019).
Original GitHub issue: https://github.com/atulmy/gql-query-builder/issues/13

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
    }
  }
}
Originally created by @albertalquisola on GitHub (May 28, 2019). Original GitHub issue: https://github.com/atulmy/gql-query-builder/issues/13 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 } } } ```
kerem 2026-03-15 11:29:31 +03:00
Author
Owner

@atulmy commented on GitHub (May 28, 2019):

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

<!-- gh-comment-id:496642786 --> @atulmy commented on GitHub (May 28, 2019): @bebraw / @toadkicker any ideas on Unions / Interfaces?
Author
Owner

@toadkicker commented on GitHub (May 28, 2019):

#11 will fix this =)

<!-- gh-comment-id:496664171 --> @toadkicker commented on GitHub (May 28, 2019): #11 will fix this =)
Author
Owner

@atulmy commented on GitHub (May 28, 2019):

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

<!-- gh-comment-id:496664735 --> @atulmy commented on GitHub (May 28, 2019): @toadkicker Can you give an example here for @albertalquisola use case?
Author
Owner

@toadkicker commented on GitHub (May 28, 2019):

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.

<!-- gh-comment-id:496666875 --> @toadkicker commented on GitHub (May 28, 2019): 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.
Author
Owner

@albertalquisola commented on GitHub (Jun 1, 2019):

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

<!-- gh-comment-id:497982549 --> @albertalquisola commented on GitHub (Jun 1, 2019): 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
Author
Owner

@toadkicker commented on GitHub (Jun 2, 2019):

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.

<!-- gh-comment-id:497994234 --> @toadkicker commented on GitHub (Jun 2, 2019): 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.
Author
Owner

@albertalquisola commented on GitHub (Jun 3, 2019):

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

<!-- gh-comment-id:498309190 --> @albertalquisola commented on GitHub (Jun 3, 2019): @toadkicker, I get the concept behind the interface adapter, but could you post a pseudocode example?
Author
Owner

@toadkicker commented on GitHub (Jun 5, 2019):

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
    )} }`;
}
<!-- gh-comment-id:499190075 --> @toadkicker commented on GitHub (Jun 5, 2019): 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 )} }`; } ```
Author
Owner

@atulmy commented on GitHub (Jun 10, 2019):

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

<!-- gh-comment-id:500382286 --> @atulmy commented on GitHub (Jun 10, 2019): @albertalquisola let us know if the adapter example was of help
Author
Owner

@Wurielle commented on GitHub (Mar 29, 2021):

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

<!-- gh-comment-id:809377737 --> @Wurielle commented on GitHub (Mar 29, 2021): Not too pretty but it does the trick: - Create a custom adapter for the type of operation you want (based on [DefaultQueryAdapter](https://github.com/atulmy/gql-query-builder/blob/master/src/adapters/DefaultQueryAdapter.ts) in my case) - Replace the imports accordingly: ```javascript // 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: ```javascript // 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: ```javascript 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](https://gist.github.com/Wurielle/53a76c256f7d1a5204130bd69e0a8601)
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/gql-query-builder#102
No description provided.