

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.

# Esquemas de GraphQL
<a name="schema-components"></a>

El esquema GraphQL es la base de una API de GraphQL. Sirve como esquema que define la forma de los datos. También es un contrato entre el cliente y el servidor que define cómo se recuperarán y modificarán los datos. and/or 

Los esquemas de GraphQL se escriben en el *lenguaje de definición de esquemas* (SDL). SDL está compuesto por tipos y campos con una estructura establecida:
+ **Tipos**: los tipos son la forma en que GraphQL define la forma y el comportamiento de los datos. GraphQL admite una multitud de tipos que se explicarán más adelante en esta sección. Cada tipo que se defina en su esquema tendrá su propio ámbito. Dentro del ámbito habrá uno o más campos que pueden contener un valor o una lógica que se utilice en el servicio GraphQL. Los tipos cumplen muchos roles diferentes, siendo las más comunes los objetos o los escalares (tipos de valores primitivos).
+ **Campos**: los campos existen dentro del ámbito de un tipo y contienen el valor que se solicita al servicio GraphQL. Se parecen mucho a las variables de otros lenguajes de programación. La forma de los datos que defina en sus campos determinará cómo se estructurarán los datos en una request/response operación. Esto permite a los desarrolladores predecir lo que se va a devolver sin saber cómo se implementa el backend del servicio.

Para visualizar el aspecto que tendría un esquema, revisemos el contenido de un esquema de GraphQL simple. En el código de producción, el esquema normalmente estará en un archivo llamado `schema.graphql` o `schema.json`. Supongamos que estamos analizando un proyecto que implementa un servicio de GraphQL. Este proyecto almacena los datos del personal de la empresa y el archivo `schema.graphql` se utiliza para recuperar los datos del personal y añadir personal nuevo a una base de datos. El código tiene este aspecto:

------
#### [ schema.graphql ]

```
type Person {                                  
   id: ID!
   name: String                                  
   age: Int
}
type Query {                                   
  people: [Person]
}
type Mutation {
  addPerson(id: ID!, name: String, age: Int): Person
}
```

------

Podemos ver que hay tres tipos definidos en el esquema: `Person`, `Query` y `Mutation`. Si nos fijamos en `Person`, podemos adivinar que es el esquema de la instancia del empleado de una empresa, lo que convertiría a este tipo en un objeto. Dentro de su alcance, vemos `id`, `name` y `age`. Estos son los campos que definen las propiedades de una `Person`. Esto significa que nuestro origen de datos almacena el `name` de cada `Person` como un tipo escalar (primitivo) `String` y `age` como un tipo escalar (primitivo) `Int`. El `id` actúa como identificador único y especial para cada `Person`. También es un valor obligatorio, tal como lo indica el símbolo `!`.

Los dos tipos de objetos siguientes se comportan de forma diferente. GraphQL reserva unas palabras clave para tipos de objetos especiales que definen cómo se rellenarán los datos en el esquema. Un tipo `Query` recuperará los datos del origen. En nuestro ejemplo, la consulta podría recuperar objetos de `Person` de una base de datos. Esto puede recordarle a las `GET` operaciones RESTful terminológicas. Una `Mutation` modificará los datos. En nuestro ejemplo, la mutación puede añadir más objetos de `Person` a la base de datos. Esto puede recordarle a operaciones que cambian de estado, como `PUT` o `POST`. Los comportamientos de todos los tipos de objetos especiales se explicarán más adelante en esta sección.

Supongamos que la `Query` de nuestro ejemplo va a recuperar algo de la base de datos. Si nos fijamos en los campos de `Query`, vemos uno llamado `people`. El valor del campo es `[Person]`. Esto significa que queremos recuperar alguna instancia de `Person` de la base de datos. Sin embargo, la adición de corchetes significa que queremos devolver una lista de todas las instancias de `Person` y no solo una específica.

El tipo `Mutation` es responsable de realizar operaciones que cambian el estado, como la modificación de datos. Una mutación es responsable de realizar alguna operación de cambio de estado en el origen de datos. En nuestro ejemplo, nuestra mutación contiene una operación llamada `addPerson` que agrega un nuevo objeto de `Person` a la base de datos. La mutación utiliza una `Person` y espera una entrada para los campos `id`, `name` y `age`.

En este punto, quizás se pregunte cómo funcionan las operaciones como `addPerson` sin una implementación de código, dado que en teoría tiene algún comportamiento y se parece mucho a una función con un nombre y parámetros de función. Actualmente, no funcionará porque un esquema solo sirve como declaración. Para implementar el comportamiento de `addPerson`, deberíamos agregarle un solucionador. Un solucionador es una unidad de código que se ejecuta siempre que se llama a su campo asociado (en este caso, la operación `addPerson`). Si quiere usar una operación, deberá añadir la implementación del solucionador en algún momento. En cierto modo, puede pensarse en la operación de esquema como la declaración de la función y en el solucionador como en la definición. Los solucionadores se explicarán en otra sección.

En este ejemplo se muestran solo las formas más sencillas en que un esquema puede manipular datos. Creará aplicaciones complejas, sólidas y escalables gracias a las funciones de GraphQL y AWS AppSync. En la siguiente sección, definiremos todos los diferentes tipos y comportamientos de campo que puede utilizar en su esquema.

# Tipos de GraphQL
<a name="graphql-types"></a>

GraphQL admite muchos tipos diferentes. Como vio en la sección anterior, los tipos definen la forma o el comportamiento de los datos. Son los componentes fundamentales de un esquema de GraphQL. 

Los tipos se pueden clasificar como entradas y salidas. Los de entrada son tipos que se pueden pasar como argumento para los tipos de objetos especiales (`Query`, `Mutation`, etc.), mientras que los tipos de salida se utilizan estrictamente para almacenar y devolver datos. A continuación, se muestra una lista de tipos y sus categorizaciones:
+ **Objetos**: un objeto contiene campos que describen una entidad. Por ejemplo, un objeto podría ser algo así como un `book` con campos que describen sus características, como `authorName`, `publishingYear`, etc. Son estrictamente tipos de salida.
+ **Escalares**: son tipos primitivos como int, string, etc. Por lo general, se asignan a los campos. Usando el campo `authorName` como ejemplo, se le podría asignar el escalar `String` para almacenar un nombre como «John Smith». Los escalares pueden ser tanto de entrada como de salida.
+ **Entradas**: las entradas permiten transferir un grupo de campos como argumento. Están estructurados de forma muy similar a los objetos, pero se pueden transferir como argumentos a objetos especiales. Las entradas permiten definir escalares, enumeraciones y otras entradas incluidas en su ámbito. Las entradas solo pueden ser tipos de entrada.
+ **Objetos especiales**: los objetos especiales realizan operaciones que cambian de estado y se encargan de la mayor parte del trabajo pesado del servicio. Hay tres tipos de objetos especiales: consulta, mutación y suscripción. Las consultas suelen obtener datos; las mutaciones manipulan los datos; las suscripciones se abren y mantienen una conexión bidireccional entre los clientes y los servidores para una comunicación constante. Los objetos especiales no son ni entradas ni salidas debido a su funcionalidad.
+ **Enumeraciones**: listas predefinidas de valores legales. Si llama a una enumeración, sus valores solo pueden ser lo que esté definido en su ámbito. Por ejemplo, si tuviera una enumeración llamada `trafficLights` que representara una lista de señales de tráfico, podría tener valores como `redLight` y `greenLight`, pero no `purpleLight`. Un semáforo real solo tendrá un número determinado de señales, por lo que podría usar la enumeración para definirlas y hacer que sean los únicos valores legales para referirse a `trafficLight`. Las enumeraciones pueden ser tanto de entrada como de salida.
+ **Uniones/interfaces**: las uniones permiten devolver uno o más elementos en una solicitud en función de los datos que haya solicitado el cliente. Por ejemplo, si tuviera un tipo `Book` con un campo `title` y un tipo `Author` con un campo `name`, podría crear una unión entre ambos tipos. Si su cliente quisiera consultar la frase “Julio César” en una base de datos, la unión podría devolver *Julio César* (la obra de William Shakespeare) del `title` del `Book` y *Julio César* (el autor de *Commentarii de Bello Gallico*) del `name` de `Author`. Las uniones solo pueden ser tipos de salida.

  Las interfaces son conjuntos de campos que los objetos deben implementar. Es parecido a las interfaces de lenguajes de programación como Java, donde hay que implementar los campos definidos en la interfaz. Por ejemplo, supongamos que creó una interfaz llamada `Book` que contenía un campo `title`. Digamos que más tarde ha creado un tipo llamado `Novel` que ha implementado `Book`. `Novel` tendría que incluir un campo `title`. Sin embargo, `Novel` también podría incluir otros campos que no estén en la interfaz, como `pageCount` o `ISBN`. Las interfaces solo pueden ser tipos de salida.

En las siguientes secciones se explicará cómo funciona cada tipo en GraphQL.

## Objects
<a name="object-components"></a>

Los objetos de GraphQL son el tipo principal que verá en el código de producción. En GraphQL, puede pensar en un objeto como en una agrupación de campos diferentes (de forma aprecia a las variables en otros lenguajes) en la que cada campo se define por un tipo (normalmente un escalar u otro objeto) que puede contener un valor. Los objetos representan una unidad de datos que puede provenir retrieved/manipulated de la implementación del servicio.

Los tipos de objetos se declaran mediante la palabra clave `Type`. Modifiquemos un poco nuestro ejemplo de esquema:

```
type Person {
  id: ID!
  name: String
  age: Int
  occupation: Occupation
}

type Occupation {
  title: String
}
```

Los tipos de objetos que aparecen aquí son `Person` y `Occupation`. Cada objeto tiene sus propios campos con sus propios tipos. Una característica de GraphQL es la capacidad de establecer campos de otros tipos. Puede ver que el campo `occupation` de `Person` contiene un tipo de objeto `Occupation`. Podemos hacer esta asociación porque GraphQL solo describe los datos y no la implementación del servicio.

## Escalares
<a name="scalar-components"></a>

Los escalares son esencialmente tipos primitivos que contienen valores. En AWS AppSync, hay dos tipos de escalares: los escalares y los AWS AppSync escalares predeterminados de GraphQL. Los escalares se utilizan normalmente para almacenar valores de campo dentro de los tipos de objetos. Entre los tipos de GraphQL predeterminados se incluyen `Int`, `Float`, `String`, `Boolean` y `ID`. Volvamos al ejemplo anterior:

```
type Person { 
  id: ID!
  name: String
  age: Int
  occupation: Occupation
}

type Occupation {
  title: String
}
```

Si elegimos los campos `title` y `name`, ambos contienen un escalar `String`. `Name` podría devolver un valor de cadena como "`John Smith`" y el título podría devolver algo como "`firefighter`". Algunas implementaciones de GraphQL también admiten escalares personalizados que utilizan la palabra clave `Scalar` e implementan el comportamiento del tipo. Sin embargo, AWS AppSync actualmente **no admite** escalares personalizados. Para obtener una lista de escalares, consulte [Tipos escalares en AWS AppSync](https://docs.aws.amazon.com//appsync/latest/devguide/scalars.html).

## Entradas
<a name="input-components"></a>

Debido al concepto de tipos de entrada y salida, existen ciertas restricciones para la transferencia de argumentos. Los tipos que normalmente deben transferirse, especialmente los objetos, están restringidos. Puede usar el tipo de entrada para omitir esta regla. Las entradas son tipos que contienen escalares, enumeraciones y otros tipos de entrada.

Las entradas se definen mediante la palabra clave `input`:

```
type Person { 
  id: ID!
  name: String
  age: Int
  occupation: Occupation
}

type Occupation {
  title: String
}

input personInput { 
  id: ID!
  name: String
  age: Int
  occupation: occupationInput
}

input occupationInput {
  title: String
}
```

Como ve, podemos tener entradas independientes que imitan el tipo original. Estas entradas se utilizarán a menudo en sus operaciones de campo de la siguiente manera:

```
type Person { 
  id: ID!
  name: String
  age: Int
  occupation: Occupation
}

type Occupation {
  title: String
}

input occupationInput {
  title: String
}

type Mutation {
  addPerson(id: ID!, name: String, age: Int, occupation: occupationInput): Person
}
```

Observe cómo seguimos transfiriendo `occupationInput` en lugar de `Occupation` para crear una `Person`. 

Este es solo uno de los escenarios sobre entradas. No es necesario que copien los objetos a escala 1:1 y, en el código de producción, lo más probable es que no lo utilice de esta manera. Una buena práctica es aprovechar los esquemas de GraphQL y definir solo lo que se necesite introducir como argumentos.

Además, se pueden usar las mismas entradas en varias operaciones, pero no recomendamos hacerlo. Lo ideal es que cada operación contenga su propia copia única de las entradas en caso de que cambien los requisitos del esquema.

## Objetos especiales
<a name="special-object-components"></a>

GraphQL reserva algunas palabras clave para objetos especiales que definen parte de la lógica empresarial sobre la forma en que el esquema generará retrieve/manipulate los datos. Como máximo, puede haber una de cada una de estas palabras clave en un esquema. Actúan como puntos de entrada para todos los datos solicitados que sus clientes ejecutan en el servicio de GraphQL. 

Los objetos especiales también se definen con la palabra clave `type`. Aunque se utilizan de forma diferente a los tipos de objetos normales, su implementación es muy parecida.

------
#### [ Queries ]

Las consultas son muy similares a las operaciones `GET` en el sentido de que realizan una búsqueda de solo lectura para obtener datos de su origen. En GraphQL, la `Query` define todos los puntos de entrada para los clientes que realizan solicitudes a su servidor. Siempre habrá una `Query` en tu implementación de GraphQL.

Aquí están la `Query` y los tipos de objetos modificados que hemos utilizado en nuestro ejemplo de esquema anterior:

```
type Person { 
  id: ID!
  name: String
  age: Int
  occupation: Occupation
}
type Occupation {
  title: String
}
type Query {                                   
  people: [Person]
}
```

Nuestra `Query` contiene un campo llamado `people` que devuelve una lista de instancias de `Person` del origen de datos. Supongamos que necesitamos cambiar el comportamiento de nuestra aplicación y que ahora necesitamos devolver una lista de solo las instancias de `Occupation` para un propósito diferente. Simplemente podríamos añadirlo a la consulta:

```
type Query {                                   
  people: [Person]
  occupations: [Occupation]
}
```

En GraphQL, podemos tratar nuestra consulta como el único origen de las solicitudes. Como puedes ver, esto es potencialmente mucho más simple que RESTful las implementaciones que podrían usar diferentes puntos finales para lograr lo mismo (`.../api/1/people`y). `.../api/1/occupations`

Suponiendo que tengamos una implementación de resolución para esta consulta, ahora podemos realizar una consulta real. Si bien el tipo de `Query` existe, debemos llamarlo explícitamente para que se ejecute en el código de la aplicación. Esto se puede hacer con la palabra clave `query`:

```
query getItems {
   people {
      name
   }
   occupations {
      title
   }
}
```

Como puede ver, esta consulta se llama `getItems` y devuelve `people` (una lista de objetos de `Person`) y `occupations` (una lista de objetos de `Occupation`). En `people`, devolvemos solo el campo `name` de cada `Person`, mientras que devolvemos el campo `title` de cada `Occupation`. La respuesta puede tener un aspecto similar al siguiente:

```
{
  "data": {
    "people": [
      {
        "name": "John Smith"
      },
      {
        "name": "Andrew Miller"
      },
      .
      .
      .
    ],
    "occupations": [
      {
        "title": "Firefighter"
      },
      {
        "title": "Bookkeeper"
      },
      .
      .
      .
    ]
  }
}
```

La respuesta de ejemplo muestra cómo los datos siguen la forma de la consulta. Cada entrada recuperada aparece dentro del ámbito del campo. `people` y `occupations` devuelven los elementos como listas separadas. Si bien es útil, puede ser más conveniente modificar la consulta para que devuelva una lista con los nombres y las ocupaciones de las personas:

```
query getItems {
   people {
      name   
      occupation {
        title
      }
}
```

Se trata de una modificación legal porque nuestro tipo `Person` contiene un campo `occupation` de tipo `Occupation`. Cuando se incluye dentro del ámbito de `people`, devolvemos el `name` de cada `Person` junto con la correspondiente `Occupation` mediante `title`. La respuesta puede tener un aspecto similar al siguiente:

```
}
  "data": {
    "people": [
      {
        "name": "John Smith",
        "occupation": {
          "title": "Firefighter"
        }
      },
      {
        "name": "Andrew Miller",
        "occupation": {
          "title": "Bookkeeper"
        }
      },
      .
      .
      .
    ]
  }
}
```

------
#### [ Mutations ]

Las mutaciones son parecidas a las operaciones que cambian el estado, como `PUT` o `POST`. Realizan una operación de escritura para modificar los datos del origen y, a continuación, obtienen la respuesta. Definen los puntos de entrada para las solicitudes de modificación de datos. A diferencia de las consultas, una mutación puede incluirse en el esquema o no según las necesidades del proyecto. Esta es la mutación del ejemplo del esquema:

```
type Mutation {
  addPerson(id: ID!, name: String, age: Int): Person
}
```

El campo `addPerson` representa un punto de entrada que añade una `Person` al origen de datos. `addPerson` es el nombre del campo; `id`, `name` y `age` son los parámetros; y `Person` es el tipo de retorno. Volviendo al tipo de `Person`:

```
type Person { 
  id: ID!
  name: String
  age: Int
  occupation: Occupation
}
```

Hemos añadido el campo `occupation`. Sin embargo, no podemos establecer este campo directamente en `Occupation` porque los objetos no se pueden transferir como argumentos; son estrictamente tipos de salida. En lugar de ello, deberíamos pasar una entrada con los mismos campos que un argumento:

```
input occupationInput {
  title: String
}
```

 También podemos actualizar fácilmente nuestro `addPerson` para incluirlo como parámetro al crear nuevas instancias de `Person`:

```
type Mutation {
  addPerson(id: ID!, name: String, age: Int, occupation: occupationInput): Person
}
```

Este es el esquema actualizado:

```
type Person { 
  id: ID!
  name: String
  age: Int
  occupation: Occupation
}

type Occupation {
  title: String
}

input occupationInput {
  title: String
}

type Mutation {
  addPerson(id: ID!, name: String, age: Int, occupation: occupationInput): Person
}
```

Tenga en cuenta que `occupation` transferirá el campo `title` desde `occupationInput` para completar la creación de la `Person` objeto en lugar del objeto `Occupation` original. Suponiendo que tengamos una implementación de resolución para `addPerson`, ahora podemos realizar una mutación real. Si bien el tipo de `Mutation` existe, debemos llamarlo explícitamente para que se ejecute en el código de la aplicación. Esto se puede hacer con la palabra clave `mutation`:

```
mutation createPerson {
  addPerson(id: ID!, name: String, age: Int, occupation: occupationInput) {
    name
    age
    occupation {
      title
    }
  }
}
```

Esta mutación se llama `createPerson` y `addPerson` es la operación. Para crear una nueva `Person`, podemos introducir los argumentos para `id`, `name`, `age` y `occupation`. En el ámbito de `addPerson`, también podemos ver otros campos como `name`, `age`, etc. Esta es su respuesta; estos son los campos que se devolverán una vez finalizada la operación `addPerson`. Esta es la parte final del ejemplo:

```
mutation createPerson {
  addPerson(id: "1", name: "Steve Powers", age: "50", occupation: "Miner") {
    id
    name
    age
    occupation {
      title
    }
  }
}
```

Si se usa esta mutación, el resultado podría ser parecido a este:

```
{
  "data": {
    "addPerson": {
      "id": "1",
      "name": "Steve Powers",
      "age": "50",
      "occupation": {
        "title": "Miner"
      }
    }
  }
}
```

Como puede ver, la respuesta ha devuelto los valores que solicitamos en el mismo formato que se definió en nuestra mutación. Se recomienda devolver todos los valores que se hayan modificado para reducir la confusión y la necesidad de realizar más consultas en el futuro. Las mutaciones permiten incluir varias operaciones dentro de su ámbito. Se ejecutarán secuencialmente en el orden indicado en la mutación. Por ejemplo, si creamos otra operación denominada `addOccupation` que agregue títulos de trabajo al origen de datos, podemos llamarla así en la mutación después de `addPerson`. `addPerson` se gestionará primero y, a continuación, `addOccupation`.

------
#### [ Subscriptions ]

Las suscripciones [WebSockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications)suelen abrir una conexión bidireccional duradera entre el servidor y sus clientes. Normalmente, un cliente se suscribe o escucha al servidor. Siempre que el servidor realice un cambio en el lado del servidor o realice un evento, el cliente suscrito recibirá las actualizaciones. Este tipo de protocolo resulta útil cuando hay varios clientes suscritos y es necesario notificarles los cambios que se produzcan en el servidor o en otros clientes. Por ejemplo, las suscripciones se pueden utilizar para actualizar los feeds de las redes sociales. Puede haber dos usuarios, el usuario A y el usuario B, que estén suscritos a las actualizaciones de notificaciones automáticas cada vez que reciban mensajes directos. El usuario A del cliente A podría enviar un mensaje directo al usuario B del cliente B. El cliente del usuario A enviaría el mensaje directo, que sería procesado por el servidor. A continuación, el servidor enviaría el mensaje directo a la cuenta del usuario B y, al mismo tiempo, enviaría una notificación automática al cliente B.

Este es un ejemplo de `Subscription` que podríamos añadir al ejemplo del esquema:

```
type Subscription {                                   
  personAdded: Person
}
```

El campo `personAdded` enviará un mensaje a los clientes suscritos cada vez que `Person` se añada una nueva al nuevo origen de datos. Suponiendo que tengamos una implementación de solucionador para `personAdded`, ahora podemos usar la suscripción. Si bien el tipo de `Subscription` existe, debemos llamarlo explícitamente para que se ejecute en el código de la aplicación. Esto se puede hacer con la palabra clave `subscription`:

```
subscription personAddedOperation {
  personAdded {
    id
    name
  }
}
```

La suscripción se llama `personAddedOperation` y la operación es `personAdded`. `personAdded` devolverá los campos `id` e `name` de las nuevas instancias de `Person`. Observando el ejemplo de la mutación, añadimos una operación `Person` utilizando esta operación:

```
addPerson(id: "1", name: "Steve Powers", age: "50", occupation: "Miner")
```

Si nuestros clientes estaban suscritos a las actualizaciones de la `Person` recién añadida, es posible que vean lo siguiente cuando se ejecute `addPerson`:

```
{
  "data": {
    "personAdded": {
      "id": "1",
      "name": "Steve Powers"
    }
  }
}
```

A continuación se muestra un resumen de lo que ofrecen las suscripciones:

Las suscripciones son canales bidireccionales que permiten al cliente y al servidor recibir actualizaciones rápidas pero constantes. Por lo general, utilizan el WebSocket protocolo, que crea conexiones estandarizadas y seguras.

Las suscripciones son ágiles, ya que reducen la sobrecarga de configuración de la conexión. Una vez suscrito, un cliente puede seguir utilizando esa suscripción durante largos períodos de tiempo. Por lo general, utilizan los recursos informáticos de forma eficiente, ya que permiten a los desarrolladores personalizar la duración de la suscripción y configurar la información que se va a solicitar.

En general, las suscripciones permiten al cliente realizar varias suscripciones a la vez. Por lo que respecta AWS AppSync, las suscripciones solo se utilizan para recibir actualizaciones del AWS AppSync servicio en tiempo real. No se pueden usar para realizar consultas o mutaciones.

La principal alternativa a las suscripciones es el sondeo, que envía consultas a intervalos establecidos para solicitar datos. Este proceso suele ser menos eficiente que las suscripciones y supone una gran carga tanto para el cliente como para el backend.

------

Lo que no se ha mencionado en nuestro ejemplo de esquema es que los tipos de objetos especiales también deben definirse en una raíz de `schema`. Por lo tanto, al exportar un esquema AWS AppSync, podría tener este aspecto:

------
#### [ schema.graphql ]

```
schema {
  query: Query
  mutation: Mutation
  subscription: Subscription
}

.
.
.

type Query {                                   
  # code goes here
}
type Mutation {                                   
  # code goes here
}
type Subscription {                                   
  # code goes here
}
```

------

## Enumeraciones
<a name="enum-components"></a>

Las enumeraciones son escalares especiales que limitan los argumentos legales que un tipo o campo puede tener. Esto significa que siempre que se defina una enumeración en el esquema, su tipo o campo asociado se limitará a los valores de la enumeración. Las enumeraciones se serializan como escalares de cadena. Tenga en cuenta que los diferentes lenguajes de programación pueden gestionar las enumeraciones de GraphQL de forma diferente. Por ejemplo, JavaScript no admite enumeraciones nativas, por lo que los valores de enumeración pueden asignarse a valores int en su lugar.

Las enumeraciones se definen mediante la palabra clave `enum`: A continuación se muestra un ejemplo:

```
enum trafficSignals {
  solidRed
  solidYellow
  solidGreen
  greenArrowLeft
  ...
}
```

Al llamar a la enumeración `trafficLights`, los argumentos solo pueden ser `solidRed`, `solidYellow`, `solidGreen`, etc. Es habitual usar enumeraciones para representar cosas que tienen un número distinto pero limitado de opciones.

## Uniones/interfaces
<a name="union-interface-components"></a>

Consulte [Interfaces y uniones](https://docs.aws.amazon.com/appsync/latest/devguide/interfaces-and-unions.html) en GraphQL.

# Campos de GraphQL
<a name="graphql-fields"></a>

Los campos existen dentro del ámbito de un tipo y contienen el valor que se solicita al servicio GraphQL. Se parecen mucho a las variables de otros lenguajes de programación. Por ejemplo, este es un tipo de objeto de `Person`:

```
type Person {                                  
   name: String                                  
   age: Int
}
```

En este caso, los campos son `name` y `age`, y contienen un valor `String` y `Int`, respectivamente. Los campos de objeto, como los que se muestran arriba, se pueden utilizar como entradas en los campos (operaciones) de las consultas y las mutaciones. Por ejemplo, consulte la `Query` siguiente:

```
type Query {                                   
  people: [Person]
}
```

El campo `people` solicita todas las instancias de `Person` del origen de datos. Cuando agrega o recupera una `Person` en su servidor de GraphQL, puede esperar que los datos sigan el formato de sus tipos y campos, es decir, la estructura de sus datos en el esquema determina cómo se estructurarán en su respuesta:

```
}
  "data": {
    "people": [
      {
        "name": "John Smith",
        "age": "50"
      },
      {
        "name": "Andrew Miller",
        "age": "60"
      },
      .
      .
      .
    ]
  }
}
```

Los campos desempeñan un rol importante en la estructuración de los datos. Hay un par de propiedades adicionales que se explican a continuación y que se pueden aplicar a los campos para una mayor personalización.

## Listas
<a name="list-components"></a>

Las listas devuelven todos los elementos de un tipo específico. Se puede agregar una lista al tipo de campo mediante corchetes `[]`: 

```
type Person { 
  name: String
  age: Int
}
type Query {                                   
  people: [Person]
}
```

En `Query`, los corchetes que rodean a `Person` indican que desea devolver todas las instancias de `Person` del origen de datos en forma de matriz. En la respuesta, los valores `name` y `age` de cada `Person` se devolverán como una lista única y delimitada:

```
}
  "data": {
    "people": [
      {
        "name": "John Smith",         # Data of Person 1
        "age": "50"
      },
      {
        "name": "Andrew Miller",      # Data of Person 2
        "age": "60"
      },
      .                               # Data of Person N
      .
      .
    ]
  }
}
```

No está limitado a los tipos de objetos especiales. También puede utilizar listas en los campos de los tipos de objetos normales.

## Valores no nulos
<a name="non-null-components"></a>

Los valores no nulos indican un campo que no puede ser nulo en la respuesta. Para establecer un campo como no nulo, utilice el símbolo `!`:

```
type Person { 
  name: String!
  age: Int
}
type Query {                                   
  people: [Person]
}
```

El campo `name` no puede ser explícitamente nulo. Si consultara el origen de datos y proporcionara una entrada nula para este campo, se generaría un error.

Puede combinar listas y valores no nulos. Compare estas consultas:

```
type Query {                                   
  people: [Person!]      # Use case 1
}

.
.
.

type Query {                                   
  people: [Person]!      # Use case 2
}

.
.
.

type Query {                                   
  people: [Person!]!     # Use case 3
}
```

En el caso de uso 1, la lista no puede contener elementos nulos. En el caso de uso 2, la lista en sí no se puede establecer como nula. En el caso de uso 3, la lista y sus elementos no pueden ser nulos. Sin embargo, en cualquier caso, aún puede devolver listas vacías.

Como puede ver, en GraphQL hay muchos componentes móviles. En esta sección, mostramos la estructura de un esquema simple y los diferentes tipos y campos que admite un esquema. En la sección siguiente, descubrirá los demás componentes de una API de GraphQL y cómo funcionan con el esquema.