

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

# Interfaces y uniones en GraphQL
<a name="interfaces-and-unions"></a>

El sistema de tipos de GraphQL admite las [interfaces](https://graphql.org/learn/schema/#interfaces). Una interfaz expone un determinado conjunto de campos que un tipo debe incluir para implementar la interfaz. 

El sistema de tipos GraphQL también admite las [uniones](https://graphql.org/learn/schema/#union-types). Las uniones son idénticas a las interfaces, con la excepción de que no definen un conjunto de campos común. En general, las uniones suelen ser la opción preferida frente a las interfaces cuando los tipos posibles no comparten una jerarquía lógica.

La siguiente sección es una referencia de los tipos de esquemas.

## Ejemplos de interfaces
<a name="interfaces"></a>

Podríamos representar una interfaz `Event` que represente cualquier tipo de actividad o reunión de personas. Algunos tipos de eventos posibles son `Concert`, `Conference` y `Festival`. Todos estos tipos comparten características comunes, incluidos un nombre, un lugar donde se celebra el evento, así como una fecha de inicio y de fin. Pero también tienen diferencias. Por ejemplo, una `Conference` incluye una lista de ponentes y talleres mientras que un `Concert` incluye una banda.

En el lenguaje de definición de esquema (SDL), la interfaz `Event` se define de la manera siguiente:

```
interface Event {
        id: ID!
        name : String!
        startsAt: String
        endsAt: String
        venue: Venue
        minAgeRestriction: Int
}
```

Y cada tipo implementa la interfaz `Event` del modo siguiente:

```
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]
}
```

Las interfaces son útiles para representar elementos, que pueden ser de varios tipos. Por ejemplo, podríamos buscar todos los eventos que ocurran en una ubicación específica. Agreguemos un campo `findEventsByVenue` al esquema de la siguiente manera:

```
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]
}
```

`findEventsByVenue` devuelve una lista de `Event`. Puesto que los campos de la interfaz GraphQL son comunes a todos los tipos de implementación, se puede seleccionar cualquier campo en la interfaz (`Event`, `id`, `name`, `startsAt`, `endsAt`, `venue` y `minAgeRestriction`). Además, puede obtener acceso a los campos en cualquier tipo de implementación utilizando [fragmentos](https://graphql.org/learn/queries/#fragments) de GraphQL, siempre que especifique el tipo.

Observemos un ejemplo de consulta de GraphQL que utiliza la interfaz.

```
query {
  findEventsAtVenue(venueId: "Madison Square Garden") {
    id
    name
    minAgeRestriction
    startsAt

    ... on Festival {
      performers
    }

    ... on Concert {
      performingBand
    }

    ... on Conference {
      speakers
      workshops
    }
  }
}
```

La consulta anterior da una sola lista de resultados y el servidor podría ordenar los eventos por fecha de inicio de forma predeterminada.

```
{
  "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"
        ]
      }
    ]
  }
}
```

Dado que los resultados se devuelven como un conjunto único de eventos, el uso de interfaces para representar características comunes resulta muy útil para ordenar los resultados.

## Ejemplos de uniones
<a name="unions"></a>

Como se indicó anteriormente, las uniones no definen conjuntos de campos comunes. Un resultado de búsqueda puede representar muchos tipos diferentes. Con el esquema `Event`, puede definir una unión `SearchResult` de la siguiente manera:

```
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
```

En este caso, para consultar cualquier campo en nuestra unión `SearchResult`, debe utilizar fragmentos.

```
query {
  search(query: "Madison") {
    ... on Venue {
      id
      name
      address
    }

    ... on Festival {
      id
      name
      performers
    }

    ... on Concert {
      id
      name
      performingBand
    }

    ... on Conference {
      speakers
      workshops
    }
  }
}
```

## Escriba la resolución en AWS AppSync
<a name="type-resolution-in-appsynclong"></a>

La resolución Type es el mecanismo por el que el motor GraphQL identifica un valor resuelto como un tipo de objeto determinado.

Volvamos al ejemplo de búsqueda de unión. Siempre y cuando nuestra consulta haya dado resultados, cada elemento de la lista de resultados debe presentarse como uno de los tipos posibles que ha definido la unión `SearchResult` (es decir, `Conference`, `Festival`, `Concert` o `Venue`).

Dado que la lógica para identificar un `Festival` a partir de un `Venue` o una `Conference` depende de los requisitos de la aplicación, debemos darle una pista al motor de GraphQL para que identifique nuestros tipos posibles a partir de los resultados sin procesar.

Con AWS AppSync, esta sugerencia se representa mediante un metacampo denominado`__typename`, cuyo valor corresponde al nombre del tipo de objeto identificado. `__typename`es obligatorio para los tipos de retorno que son interfaces o uniones.

## Ejemplo de resolución Type
<a name="type-resolution-example"></a>

Utilicemos el esquema anterior. Puede seguir el ejemplo navegando a la consola y añadiendo los siguientes datos en la página **Schema (Esquema)**:

```
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
}
```

A continuación, asociaremos un solucionador al campo `Query.search`. En la `Resolvers` sección, elija **Adjuntar**, cree una nueva **fuente de datos** de tipo *NONE* y, a continuación, asígnele un nombre *StubDataSource*. En este ejemplo, vamos a imaginar que hemos tomado los resultados de una fuente externa y que hemos codificado los resultados obtenidos en la plantilla de mapeo de solicitud.

En el panel de la plantilla de mapeo de solicitud, escriba lo siguiente:

```
{
    "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"]
        }
    ]
}
```

Si la aplicación devuelve el nombre del tipo como parte del campo `id`, la lógica de resolución de tipos debe analizar el campo `id` para extraer el nombre del tipo y, a continuación, añadir el campo `__typename` a cada uno de los resultados. Puede aplicar dicha lógica en la plantilla de mapeo de respuesta de la siguiente manera:

**nota**  
También puede realizar esta tarea como parte de su función de Lambda, si usa el origen de datos de Lambda.

```
#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)
```

Ejecute la siguiente consulta:

```
query {
  search(query: "Madison") {
    ... on Venue {
      id
      name
      address
    }

    ... on Festival {
        id
      name
      performers
    }

    ... on Concert {
      id
      name
      performingBand
    }

    ... on Conference {
      speakers
      workshops
    }
  }
}
```

La consulta genera los siguientes resultados:

```
{
  "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"
        ]
      }
    ]
  }
}
```

La lógica de la resolución Type varía en función de cada aplicación. Por ejemplo, podría tener una lógica de identificación distinta que compruebe la existencia de determinados campos o incluso una combinación de campos. Es decir, podría detectar la presencia del campo `performers` para identificar un `Festival` o la combinación de los campos `speakers` y `workshops` para identificar una `Conference`. En definitiva, le corresponde a usted definir la lógica que desea usar.