Interfaces and unions in GraphQL
The GraphQL type system supports Interfaces
The GraphQL type system also supports Unions
The following section is a reference for schema typing.
Interface examples
We could represent an Event interface that represents any kind of activity or gathering of
      people. Some possible event types are Concert, Conference, and Festival.
      These types all share common characteristics, including a name, a venue where the event is taking place, and a
      start and end date. These types also have differences; a Conference offers a list of speakers and
      workshops, while a Concert features a performing band.
In Schema Definition Language (SDL), the Event interface is defined as follows:
interface Event { id: ID! name : String! startsAt: String endsAt: String venue: Venue minAgeRestriction: Int }
And each of the types implements the Event interface as follows:
type Concert implements Event { id: ID! name: String! startsAt: String endsAt: String venue: Venue minAgeRestriction: Int performingBand: String } type Festival implements Event { id: ID! name: String! startsAt: String endsAt: String venue: Venue minAgeRestriction: Int performers: [String] } type Conference implements Event { id: ID! name: String! startsAt: String endsAt: String venue: Venue minAgeRestriction: Int speakers: [String] workshops: [String] }
Interfaces are useful to represent elements that might be of several types. For example, we could search for
      all events happening at a specific venue. Let’s add a findEventsByVenue field to the schema as
      follows:
schema { query: Query } type Query { # Retrieve Events at a specific Venue findEventsAtVenue(venueId: ID!): [Event] } type Venue { id: ID! name: String address: String maxOccupancy: Int } type Concert implements Event { id: ID! name: String! startsAt: String endsAt: String venue: Venue minAgeRestriction: Int performingBand: String } interface Event { id: ID! name: String! startsAt: String endsAt: String venue: Venue minAgeRestriction: Int } type Festival implements Event { id: ID! name: String! startsAt: String endsAt: String venue: Venue minAgeRestriction: Int performers: [String] } type Conference implements Event { id: ID! name: String! startsAt: String endsAt: String venue: Venue minAgeRestriction: Int speakers: [String] workshops: [String] }
The findEventsByVenue returns a list of Event. Because GraphQL interface fields are
      common to all the implementing types, it’s possible to select any fields on the Event interface
        (id, name, startsAt, endsAt, venue, and
        minAgeRestriction). Additionally, you can access the fields on any implementing type using GraphQL
        fragments
Let’s examine an example of a GraphQL query that uses the interface.
query { findEventsAtVenue(venueId: "Madison Square Garden") { id name minAgeRestriction startsAt ... on Festival { performers } ... on Concert { performingBand } ... on Conference { speakers workshops } } }
The previous query yields a single list of results, and the server could sort the events by start date by default.
{ "data": { "findEventsAtVenue": [ { "id": "Festival-2", "name": "Festival 2", "minAgeRestriction": 21, "startsAt": "2018-10-05T14:48:00.000Z", "performers": [ "The Singers", "The Screamers" ] }, { "id": "Concert-3", "name": "Concert 3", "minAgeRestriction": 18, "startsAt": "2018-10-07T14:48:00.000Z", "performingBand": "The Jumpers" }, { "id": "Conference-4", "name": "Conference 4", "minAgeRestriction": null, "startsAt": "2018-10-09T14:48:00.000Z", "speakers": [ "The Storytellers" ], "workshops": [ "Writing", "Reading" ] } ] } }
Since results are returned as a single collection of events, using interfaces to represent common characteristics is very helpful for sorting results.
Union examples
As stated earlier, unions don't define common sets of fields. A search result might represent many different
      types. Using the Event schema, you can define a SearchResult union as follows:
type Query { # Retrieve Events at a specific Venue findEventsAtVenue(venueId: ID!): [Event] # Search across all content search(query: String!): [SearchResult] } union SearchResult = Conference | Festival | Concert | Venue
In this case, to query any field on our SearchResult union, you must use fragments:
query { search(query: "Madison") { ... on Venue { id name address } ... on Festival { id name performers } ... on Concert { id name performingBand } ... on Conference { speakers workshops } } }
Type resolution in AWS AppSync
Type resolution is the mechanism by which the GraphQL engine identifies a resolved value as a specific object type.
Going back to the union search example, provided our query yielded results, each item in the results list must
      present itself as one of the possible types that the SearchResult union defined (that is,
        Conference, Festival, Concert, or Venue).
Because the logic to identify a Festival from a Venue or a Conference
      is dependent on the application requirements, the GraphQL engine must be given a hint to identify our possible
      types from the raw results.
With AWS AppSync, this hint is represented by a meta field named __typename, whose value
      corresponds to the identified object type name. __typename is required for return types that are
      interfaces or unions.
Type resolution example
Let’s reuse the previous schema. You can follow along by navigating to the console and adding the following under the Schema page:
schema { query: Query } type Query { # Retrieve Events at a specific Venue findEventsAtVenue(venueId: ID!): [Event] # Search across all content search(query: String!): [SearchResult] } union SearchResult = Conference | Festival | Concert | Venue type Venue { id: ID! name: String! address: String maxOccupancy: Int } interface Event { id: ID! name: String! startsAt: String endsAt: String venue: Venue minAgeRestriction: Int } type Festival implements Event { id: ID! name: String! startsAt: String endsAt: String venue: Venue minAgeRestriction: Int performers: [String] } type Conference implements Event { id: ID! name: String! startsAt: String endsAt: String venue: Venue minAgeRestriction: Int speakers: [String] workshops: [String] } type Concert implements Event { id: ID! name: String! startsAt: String endsAt: String venue: Venue minAgeRestriction: Int performingBand: String }
Let’s attach a resolver to the Query.search field. In the Resolvers section, choose
        Attach, create a new Data Source of type
        NONE, and then name it StubDataSource. For the sake of this example,
      we’ll pretend we fetched results from an external source, and hard code the fetched results in the request mapping
      template.
In the request mapping template pane, enter the following:
{ "version" : "2018-05-29", "payload": ## We are effectively mocking our search results for this example [ { "id": "Venue-1", "name": "Venue 1", "address": "2121 7th Ave, Seattle, WA 98121", "maxOccupancy": 1000 }, { "id": "Festival-2", "name": "Festival 2", "performers": ["The Singers", "The Screamers"] }, { "id": "Concert-3", "name": "Concert 3", "performingBand": "The Jumpers" }, { "id": "Conference-4", "name": "Conference 4", "speakers": ["The Storytellers"], "workshops": ["Writing", "Reading"] } ] }
If the application returns the type name as part of the id field, the type resolution logic must
      parse the id field to extract the type name and then add the __typename field to each of
      the results. You can perform that logic in the response mapping template as follows:
Note
You can also perform this task as part of your Lambda function, if you are using the Lambda data source.
#foreach ($result in $context.result) ## Extract type name from the id field. #set( $typeName = $result.id.split("-")[0] ) #set( $ignore = $result.put("__typename", $typeName)) #end $util.toJson($context.result)
Run the following query:
query { search(query: "Madison") { ... on Venue { id name address } ... on Festival { id name performers } ... on Concert { id name performingBand } ... on Conference { speakers workshops } } }
The query yields the following results:
{ "data": { "search": [ { "id": "Venue-1", "name": "Venue 1", "address": "2121 7th Ave, Seattle, WA 98121" }, { "id": "Festival-2", "name": "Festival 2", "performers": [ "The Singers", "The Screamers" ] }, { "id": "Concert-3", "name": "Concert 3", "performingBand": "The Jumpers" }, { "speakers": [ "The Storytellers" ], "workshops": [ "Writing", "Reading" ] } ] } }
The type resolution logic varies depending on the application. For example, you could have a different
      identifying logic that checks for the existence of certain fields or even a combination of fields. That is, you
      could detect the presence of the performers field to identify a Festival or the
      combination of the speakers and the workshops fields to identify a
        Conference. Ultimately, it is up to you to define the logic you want to use.