

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.

# GraphQL y arquitectura AWS AppSync
<a name="graphql-overview"></a>

**nota**  
Esta guía asume que el usuario tiene un conocimiento práctico del estilo arquitectónico REST. Recomendamos revisar este y otros temas de frontend antes de trabajar con GraphQL y AWS AppSync.

GraphQL es un lenguaje de consulta y manipulación para. APIs GraphQL proporciona una sintaxis flexible e intuitiva para describir los requisitos e interacciones de los datos. Permite a los desarrolladores solicitar exactamente lo que se necesita y obtener resultados predecibles. También permite acceder a muchos orígenes en una sola solicitud, lo que reduce la cantidad de llamadas de red y los requisitos de ancho de banda y, por lo tanto, ahorra batería y los ciclos de CPU que las aplicaciones consumen. 

La actualización de los datos se simplifica con mutaciones, lo que permite a los desarrolladores describir cómo deberían cambiar los datos. GraphQL también facilita la configuración rápida de soluciones en tiempo real mediante suscripciones. Todas estas características combinadas, junto con potentes herramientas para desarrolladores, hacen que GraphQL sea esencial para administrar los datos de las aplicaciones.

GraphQL es una alternativa a REST. RESTful La arquitectura es actualmente una de las soluciones más populares para la comunicación cliente-servidor. Se centra en el concepto de que sus recursos (datos) están expuestos por una URL. Se URLs pueden usar para acceder a los datos y manipularlos mediante operaciones CRUD (crear, leer, actualizar, eliminar) en forma de métodos HTTP como`GET`, `POST` y. `DELETE` La ventaja de REST es que es relativamente fácil de aprender e implementar. Puede configurarlo rápidamente RESTful APIs para llamar a una amplia gama de servicios. 

Sin embargo, la tecnología se complica cada vez más. A medida que las aplicaciones, las herramientas y los servicios comienzan a escalarse para un público de alcance mundial, la necesidad de arquitecturas rápidas y escalables adquiere una importancia capital. REST tiene muchas deficiencias en cuanto a operaciones escalables. Vea este [caso de uso](https://aws.amazon.com/blogs/architecture/what-to-consider-when-modernizing-apis-with-graphql-on-aws/) como ejemplo.

En las siguientes secciones, repasaremos algunos de los conceptos que lo rodean RESTful APIs. A continuación, presentaremos GraphQL y cómo funciona.

Para obtener más información sobre GraphQL y las ventajas de migrar a AWS, consulta la Guía de [decisiones sobre las implementaciones de GraphQL](https://aws.amazon.com/graphql/guide/).

**Topics**
+ [Qué es una API](what-is-an-api.md)
+ [Qué es REST](what-is-rest.md)
+ [Qué es GraphQL](what-is-graphql.md)
+ [Comparación entre REST y GraphQL](comparing-rest-graphql.md)
+ [Por qué usar GraphQL en lugar de REST](why-use-graphql.md)
+ [Componentes de una API de GraphQL](api-components.md)
+ [Propiedades adicionales de GraphQL](graphql-properties.md)

# ¿Qué es una API?
<a name="what-is-an-api"></a>

Una interfaz de programación de aplicaciones (API) define las reglas que se deben seguir para comunicarse con otros sistemas de software. Los desarrolladores exponen o crean para APIs que otras aplicaciones puedan comunicarse con sus aplicaciones mediante programación. Por ejemplo, la aplicación de plantillas de horarios expone una API que solicita el nombre completo del empleado y un intervalo de fechas. Cuando recibe esta información, procesa internamente la plantilla de horarios del empleado y devuelve el número de horas trabajadas en ese intervalo de fechas.

Puede pensar en una API web como una puerta de enlace entre los clientes y los recursos de la web.

## Clientes
<a name="what-is-a-client"></a>

Los clientes son usuarios que desean acceder a la información desde la web. El cliente puede ser una persona o un sistema de software que utilice la API. Por ejemplo, los desarrolladores pueden escribir programas que accedan a la información del tiempo de un sistema meteorológico. O se puede acceder a los mismos datos desde un navegador al visitar directamente el sitio web meteorológico.

## Recursos
<a name="what-is-a-resource"></a>

Los recursos son la información que las diferentes aplicaciones proporcionan a sus clientes. Los recursos pueden ser imágenes, vídeos, texto, números o cualquier tipo de datos. La máquina que proporciona el recurso al cliente también se denomina servidor. Las organizaciones APIs lo utilizan para compartir recursos y proporcionar servicios web a la vez que mantienen la seguridad, el control y la autenticación. Además, APIs ayúdelas a determinar qué clientes tienen acceso a recursos internos específicos.

# ¿Qué es REST?
<a name="what-is-rest"></a>

A un alto nivel, la transferencia de estado representacional (REST) es una arquitectura de software que impone condiciones sobre el funcionamiento de una API. REST se creó inicialmente como una guía para gestionar la comunicación en una red compleja como Internet. Una arquitectura basada en REST puede emplearse para apoyar una comunicación fiable y de alto rendimiento a escala. Puede implementarla y modificarla fácilmente, lo que aporta visibilidad y portabilidad multiplataforma a cualquier sistema de API.

Los desarrolladores de API pueden diseñar APIs utilizando varias arquitecturas diferentes. APIs las que siguen el estilo arquitectónico REST se denominan REST APIs. Los servicios web que implementan la arquitectura REST se denominan servicios RESTful web. El término RESTful API generalmente se refiere a la RESTful web APIs. Sin embargo, puede utilizar los términos API REST y RESTful API indistintamente.

A continuación, se indican algunos de los principios del estilo arquitectónico de REST:

## Interfaz uniforme
<a name="uniform-interface"></a>

La interfaz uniforme es fundamental para el diseño de cualquier RESTful servicio web. Indica que el servidor transfiere la información en un formato estándar. El recurso formateado se denomina representación en REST. Este formato puede ser diferente de la representación interna del recurso en la aplicación de servidor. Por ejemplo, el servidor puede almacenar datos como texto pero enviarlos en un formato de representación HTML.

La interfaz uniforme impone cuatro restricciones arquitectónicas:

1.  Las solicitudes deben identificar recursos. Lo hacen mediante un identificador de recursos uniforme. 

1.  Los clientes tienen suficiente información en la representación del recurso para modificarlo o eliminarlo si así lo desean. El servidor cumple esta condición mediante el envío de metadatos que describen el recurso con más detalle. 

1.  Los clientes reciben información sobre cómo seguir procesando la representación. El servidor lo consigue enviando mensajes autodescriptivos que contienen metadatos sobre la mejor manera en la que el cliente puede utilizarlos. 

1.  Los clientes reciben información sobre todos los demás recursos relacionados que necesitan para completar una tarea. El servidor lo consigue enviando hipervínculos en la representación para que los clientes puedan descubrir más recursos de forma dinámica. 

## Ausencia de estado
<a name="statelessness"></a>

En la arquitectura REST, la ausencia de estado se refiere a un método de comunicación en el que el servidor completa cada una de las solicitudes del cliente independientemente de las solicitudes anteriores. Los clientes pueden solicitar recursos en cualquier orden y cada solicitud no tiene estado o está aislada de las otras. Esta restricción de diseño de la API de REST implica que el servidor puede comprender y cumplir completamente la solicitud en todo momento. 

## Sistema por capas
<a name="layered-system"></a>

En una arquitectura de sistema por capas, el cliente puede conectarse a otros intermediarios autorizados entre el cliente y el servidor y seguirá recibiendo respuestas del servidor. Los servidores también pueden transferir las solicitudes a otros servidores. Puede diseñar su servicio RESTful web para que se ejecute en varios servidores con varios niveles, como seguridad, aplicaciones y lógica empresarial, y trabajar juntos para cumplir con las solicitudes de los clientes. Estas capas permanecen invisibles para el cliente.

## Capacidad de almacenamiento en caché
<a name="cacheability"></a>

RESTful Los servicios web admiten el almacenamiento en caché, que es el proceso de almacenar algunas respuestas en el cliente o en un intermediario para mejorar el tiempo de respuesta del servidor. Por ejemplo, supongamos que visita un sitio web que tiene imágenes comunes de encabezado y pie de página en todas las páginas. Cada vez que visita una nueva página web, el servidor debe volver a enviar las mismas imágenes. Para evitarlo, el cliente guarda en caché o almacena estas imágenes después de la primera respuesta y, a continuación, las utiliza directamente desde la memoria caché. RESTful Los servicios web controlan el almacenamiento en caché mediante respuestas de API que se definen a sí mismas como almacenables en caché o no almacenables en caché.

## ¿Qué es una API? RESTful
<a name="what-is-a-restful-api"></a>

RESTful La API es una interfaz que utilizan dos sistemas informáticos para intercambiar información de forma segura a través de Internet. La mayoría de las aplicaciones empresariales tienen que comunicarse con otras aplicaciones internas y de terceros para realizar diversas tareas. Por ejemplo, para generar recibos de pago mensuales, su sistema de cuentas interno debe compartir datos con el sistema bancario de su cliente para automatizar la facturación y comunicarse con una aplicación interna de registro de horas. RESTful APIs respaldan este intercambio de información porque siguen estándares de comunicación de software seguros, confiables y eficientes.

## ¿Cómo RESTful APIs funcionan?
<a name="how-do-restful-apis-work"></a>

La función básica de una RESTful API es la misma que la de navegar por Internet. El cliente contacta con el servidor mediante la API cuando necesita un recurso. Los desarrolladores de las API explican cómo el cliente debe usar la API de REST en la documentación de la API de la aplicación de servidor. Estos son los pasos generales para cualquier llamada a la API de REST:

1.  El cliente envía una solicitud al servidor. El cliente sigue la documentación de la API para aplicar a la solicitud un formato que el servidor comprenda. 

1.  El servidor autentica al cliente y confirma que este tiene derecho a realizar la solicitud. 

1.  El servidor recibe la solicitud y la procesa internamente. 

1.  El servidor devuelve una respuesta al cliente. La respuesta contiene información que indica al cliente si la solicitud se ha realizado correctamente. La respuesta también incluye la información que el cliente haya solicitado. 

Los detalles de solicitud y respuesta de la API de REST varían ligeramente según cómo diseñen la API los desarrolladores.

# ¿Qué es GraphQL?
<a name="what-is-graphql"></a>

GraphQL es tanto un lenguaje de consulta como un tiempo de ejecución para ejecutar esas consultas. APIs GraphQL permite a los clientes solicitar exactamente los datos que necesitan, lo que ofrece una alternativa más flexible y eficiente a REST en muchos escenarios. A diferencia de REST, que se basa en puntos de conexión predefinidos, GraphQL utiliza un único punto de conexión en el que los clientes pueden especificar sus requisitos de datos en forma de consultas y mutaciones. 

Consulte [Componentes de una API de GraphQL](https://docs.aws.amazon.com/appsync/latest/devguide/api-components.html) para obtener más información sobre cómo se estructuran los APIs GraphQL.

# Comparación entre REST y GraphQL
<a name="comparing-rest-graphql"></a>

APIs (Las interfaces de programación de aplicaciones) desempeñan un papel crucial a la hora de facilitar el intercambio de datos entre aplicaciones. Como se indicó anteriormente, APIs han surgido dos enfoques destacados para el diseño: GraphQL y REST. Si bien ambos cumplen el propósito fundamental de permitir la comunicación entre el cliente y el servidor, difieren significativamente en su implementación y casos de uso.

GraphQL y REST comparten varias características clave: 

1. **Modelo cliente-servidor**: ambos utilizan una arquitectura cliente-servidor para el intercambio de datos. 

1. **Ausencia de estado**: ninguno de los dos mantiene la información de la sesión del cliente entre las solicitudes. 

1. **Basado en HTTP**: ambos suelen utilizar HTTP como protocolo de comunicación subyacente. 

1. **Diseño orientado a los recursos**: ambos diseñan su intercambio de datos en torno a los recursos, es decir, cualquier dato u objeto al que el cliente puede acceder y manipular a través de la API. 

1. **Flexibilidad de formato de datos**: JSON es el formato de intercambio de datos más utilizado en ambos, aunque también se admiten otros formatos, como XML y HTML. 

1. **Independencia de lenguajes y bases de datos**: ambos pueden funcionar con cualquier lenguaje de programación o estructura de base de datos, lo que los hace altamente interoperables. 

1. **Compatibilidad con almacenamiento en caché**: ambos admiten el almacenamiento en caché, lo que permite a los clientes y servidores almacenar los datos a los que se accede con frecuencia para mejorar el rendimiento. 

Si bien comparten algunos principios fundamentales, GraphQL y REST difieren significativamente en su enfoque del diseño de API y la obtención de datos:

1. **Estructura de solicitudes y obtención de datos**

   REST utiliza diferentes métodos de HTTP (GET, POST, PUT, DELETE) para realizar operaciones en los recursos. Esto suele requerir varios puntos de conexión para diferentes recursos, lo que puede provocar ineficiencias en la recuperación de datos. Por ejemplo, ejecutar una operación GET para recuperar los datos de un usuario puede provocar una obtención excesiva o insuficiente de datos. Para obtener los datos correctos, se puede llamar a una operación de truncamiento o a otras varias operaciones. 

   GraphQL utiliza un único punto de conexión para todas las operaciones. Se basa en consultas para obtener datos y mutaciones para modificarlos. Los clientes pueden utilizar las consultas para obtener exactamente los datos que necesitan en una sola solicitud, lo que reduce la sobrecarga de la red al minimizar la transferencia de datos. 

1. **Esquema del lado del servidor**

   REST no requiere un esquema del lado del servidor, aunque se puede definir uno opcionalmente para un diseño y una documentación de API eficientes.

   GraphQL utiliza un esquema del lado del servidor con establecimiento inflexible de tipos para definir los datos y los servicios de datos. El esquema, escrito en el lenguaje de definición de esquema (SDL) de GraphQL, incluye tipos de objetos y campos para cada objeto, así como funciones de solucionador del lado del servidor que definen las operaciones para cada campo.

1. **Control de versiones**

   REST suele incluir el control de versiones en la URL, lo que puede llevar a mantener varias versiones de la API simultáneamente. El control de versiones no es obligatorio, pero puede ayudar a evitar cambios importantes. 

   GraphQL promueve una evolución continua de la API sin un control de versiones explícito al requerir la compatibilidad con versiones anteriores. Los campos eliminados devuelven mensajes de error, mientras que las etiquetas de obsolescencia eliminan gradualmente los campos antiguos y devuelven mensajes de advertencia. 

1. **Gestión de errores** 

   REST tiene un establecimiento flexible de tipos, por lo que es necesario integrar la gestión de errores en el código correspondiente. Es posible que esto no identifique automáticamente los errores relacionados con los tipos (por ejemplo, analizar un número como texto). 

   Por el contrario, GraphQL tiene un establecimiento inflexible de tipos y requiere una definición de esquema completa. Esto permite que su servicio identifique automáticamente muchos errores de solicitud con un alto nivel de detalle.

1. **Casos de uso**

   REST es más adecuado para: 
   + Aplicaciones más pequeñas con requisitos de datos menos complejos. 
   + Escenarios en los que todos los clientes utilicen los datos y las operaciones de forma similar. 
   + Aplicaciones sin necesidades complejas de consulta de datos. 

   GraphQL es más adecuado para: 
   + Escenarios con un ancho de banda limitado, en los que sea crucial minimizar las solicitudes y las respuestas. 
   + Aplicaciones con múltiples orígenes de datos que deban combinarse en un único punto de conexión. 
   + Casos en los que las solicitudes de los clientes varían significativamente y esperan estructuras de respuesta diferentes.

   Ten en cuenta que es posible usar tanto GraphQL como REST APIs en una sola aplicación para diferentes áreas de funcionalidad. Además, puede actualizar una RESTful API para incluir las capacidades de GraphQL sin necesidad de una reescritura completa. Consulte [Cómo crear resolutores de GraphQL para fuentes de AWS datos para](https://aws.amazon.com/graphql/resolvers/) ver un ejemplo.

# ¿Por qué usar GraphQL en lugar de REST?
<a name="why-use-graphql"></a>

REST es uno de los estilos arquitectónicos fundamentales de la web. APIs Sin embargo, cuanto más se interconecte el mundo, la necesidad de desarrollar aplicaciones robustas y escalables se convertirá en un tema cada vez más urgente. Si bien REST se usa a menudo para crear sitios web APIs, se han identificado varios inconvenientes recurrentes en RESTful las implementaciones:

1. **Solicitudes de datos**: Al usarlo RESTful APIs, normalmente solicitarías los datos que necesitas a través de puntos finales. El problema surge cuando hay datos que pueden no estar tan bien empaquetados. Es posible que los datos que necesite estén ocultos tras múltiples niveles de abstracción y que la única forma de obtenerlos sea mediante varios puntos de conexión, lo que implica realizar diversas solicitudes para extraer todos los datos.

1. **Obtención excesiva o insuficiente de datos**: para agravar los problemas que plantean las solicitudes múltiples, los datos de cada punto de conexión están estrictamente definidos, por lo que se obtendrá como resultado todos los datos que se hayan definido para esa API, incluso si técnicamente no los deseaba.

   Esto puede provocar una *obtención excesiva de datos*, lo que significa que nuestras solicitudes devuelven datos superfluos. Por ejemplo, supongamos que solicita datos del personal de la empresa y desea saber los nombres de los empleados de un departamento determinado. El punto de conexión que devuelve los datos contendrá los nombres, pero también puede contener otras informaciones, como el cargo o la fecha de nacimiento. Como la API es fija, no se puede solicitar simplemente los nombres, sino que el resto de los datos se incluyen también.

   La situación opuesta, en la que no devolvemos suficientes datos, se denomina *obtención insuficiente de datos*. Para obtener todos los datos solicitados, tal vez deba realizar varias solicitudes al servicio. En función de cómo estén estructurados los datos, puede encontrarse con consultas ineficientes que den lugar a problemas como el temido problema n\$11.

1. **Iteraciones de desarrollo lentas**: muchos desarrolladores las adaptan RESTful APIs al flujo de sus aplicaciones. Sin embargo, a medida que sus aplicaciones crecen, es posible que tanto el front-end como el backend requieran cambios importantes. Como resultado, es APIs posible que ya no se ajusten a la forma de los datos de una manera eficiente o impactante. Esto hace que las iteraciones de los productos sean más lentas debido a la necesidad de modificar la API.

1. **Rendimiento a escala**: debido a estos problemas agravados, la escalabilidad se verá afectada en numerosos ámbitos. El rendimiento de las aplicaciones puede verse afectado porque las solicitudes devuelvan demasiados datos o muy pocos (lo que se traduce en más solicitudes). Ambas situaciones provocan una carga innecesaria en la red, lo que se traduce en un rendimiento deficiente. Por parte de los desarrolladores, es posible que la velocidad de desarrollo se reduzca porque los datos que solicitan APIs son fijos y ya no caben en ellos.

El argumento de venta de GraphQL es la superación de los inconvenientes de REST. Estas son algunas de las soluciones clave que GraphQL ofrece a los desarrolladores:

1. **Puntos de conexión únicos**: GraphQL utiliza un único punto de conexión para consultar datos. No es necesario crear varios para APIs adaptarlos a la forma de los datos. El resultado de esto es un menor número de solicitudes que circulan por la red.

1. **Obtención de datos**: GraphQL resuelve los problemas perennes de la obtención excesiva o insuficiente de datos simplemente definiendo los datos que necesita. GraphQL permite dar forma a los datos para que se ajusten a sus necesidades y solo reciba lo que haya pedido.

1. **Abstracción**: APIs GraphQL contiene algunos componentes y sistemas que describen los datos utilizando un estándar independiente del lenguaje. En otras palabras, la forma y la estructura de los datos están estandarizadas para que tanto el frontend como el backend sepan cómo se van a enviar a través de la red. Esto permite a los desarrolladores de ambas partes trabajar con los sistemas de GraphQL y no cerca de ellos.

1. **Iteraciones rápidas**: debido a la estandarización de los datos, es posible que los cambios realizados en un extremo del desarrollo no sean necesarios en el otro. Por ejemplo, es posible que los cambios de presentación del frontend no provoquen variaciones importantes en el backend, ya que GraphQL permite modificar fácilmente la especificación de los datos. Simplemente puede definir o modificar la forma de los datos para adaptarlos a las necesidades de la aplicación a medida que crecen. La consecuencia de esto es la reducción del trabajo de desarrollo potencial.

Estas son solo algunas de las ventajas de GraphQL. En las siguientes secciones, aprenderá a estructurar GraphQL y las propiedades que lo convierten en una alternativa única a REST.

# Componentes de una API de GraphQL
<a name="api-components"></a>

Una API GraphQL estándar se compone de un único esquema que gestiona la forma de los datos que se van a consultar. El esquema está vinculado a uno o más orígenes de datos como una base de datos o una función de Lambda. Entre las dos se encuentran uno o más solucionadores que se encargan de la lógica empresarial de las solicitudes. Todos los componentes tienen efectos muy relevantes en la implementación de GraphQL. En las siguientes secciones se presentarán estos tres componentes y el papel que desempeñan en el servicio GraphQL.

![\[GraphQL API components: schema, resolvers, and data sources interconnected with AppSync.\]](http://docs.aws.amazon.com/es_es/appsync/latest/devguide/images/appsync-architecture-graphql-api.png)


**Topics**
+ [Esquemas de GraphQL](schema-components.md)
+ [Orígenes de datos](data-source-components.md)
+ [Solucionadores](resolver-components.md)

# 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.

# Orígenes de datos
<a name="data-source-components"></a>

En la sección anterior, hemos aprendido que un esquema define la forma de los datos. Sin embargo, no llegamos a explicar de dónde procedían esos datos. En proyectos reales, el esquema es como una puerta de enlace que gestiona todas las solicitudes realizadas al servidor. Cuando se realiza una solicitud, el esquema actúa como el único punto de conexión que interactúa con el cliente. El esquema accederá a los datos del origen de datos, los procesará y los retransmitirá al cliente. Vea la infografía siguiente:

![\[GraphQL schema integrating multiple Servicios de AWS for a single endpoint API architecture.\]](http://docs.aws.amazon.com/es_es/appsync/latest/devguide/images/aws-flow-infographic.png)


AWS AppSync y GraphQL implementan magníficamente las soluciones Backend For Frontend (BFF). Funcionan en grupo para reducir la complejidad a gran escala al abstraer el backend. Si su servicio utiliza orígenes de datos o microservicios diferentes, para abstraer de manera básica parte de la complejidad, puede definir la forma de los datos de cada origen (subgráfico) en un único esquema (supergráfico). Esto significa que su API de GraphQL no se limita a usar un origen de datos. Puede asociar cualquier número de orígenes de datos a su API de GraphQL y especificar en su código cómo van a interactuar con el servicio.

Como puede ver en la infografía, el esquema de GraphQL contiene toda la información que los clientes necesitan para solicitar datos. Esto significa que todo se puede procesar en una sola solicitud en lugar de en varias, como es el caso con REST. Estas solicitudes pasan por el esquema, que es el único punto de conexión del servicio. Cuando se procesan las solicitudes, un solucionador (elemento que se explica en la sección siguiente) ejecuta su código para procesar los datos del origen de datos correspondiente. Cuando se devuelva la respuesta, el subgráfico vinculado al origen de datos se rellenará con los datos del esquema. 

AWS AppSync admite muchos tipos de fuentes de datos diferentes. En la siguiente tabla, describiremos cada tipo, enumeraremos algunas de las ventajas de cada uno y proporcionaremos enlaces útiles para obtener más contexto.


| Origen de datos | Description (Descripción) | Ventajas | Información complementaria | 
| --- | --- | --- | --- | 
| Amazon DynamoDB | “Amazon DynamoDB es un servicio de base de datos NoSQL totalmente administrado que ofrece un rendimiento rápido y predecible, así como una perfecta escalabilidad. DynamoDB le permite delegar las cargas administrativas que supone tener que utilizar y escalar bases de datos distribuidas, para que no tenga que preocuparse del aprovisionamiento, la instalación ni la configuración del hardware, ni tampoco de las tareas de replicación, aplicación de parches de software o escalado de clústeres. DynamoDB también ofrece el cifrado en reposo, que elimina la carga y la complejidad operativa que conlleva la protección de información confidencial.” |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/appsync/latest/devguide/data-source-components.html)  | 
| AWS Lambda | «AWS Lambda es un servicio informático que permite ejecutar código sin aprovisionar ni administrar servidores.Lambda ejecuta el código en una infraestructura de computación de alta disponibilidad y realiza todas las tareas de administración de los recursos de computación, incluido el mantenimiento del servidor y del sistema operativo, el aprovisionamiento de capacidad y el escalado automático, así como las funciones de registro. Con Lambda, lo único que tiene que hacer es suministrar el código en uno de los tiempos de ejecución de lenguaje compatibles con Lambda.” |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/appsync/latest/devguide/data-source-components.html)  | 
| OpenSearch | «Amazon OpenSearch Service es un servicio gestionado que facilita la implementación, el funcionamiento y el escalado de OpenSearch clústeres en la AWS nube. Amazon OpenSearch Service es compatible con OpenSearch Elasticsearch OSS heredado (hasta la 7.10, la versión final de código abierto del software). Al crear un clúster, tiene la opción de elegir qué motor de búsqueda utilizar.**OpenSearch**es un motor de búsqueda y análisis totalmente de código abierto para casos de uso como el análisis de registros, la supervisión de aplicaciones en tiempo real y el análisis del flujo de clics. Para obtener más información, consulte la [Documentación de OpenSearch](https://opensearch.org/docs/).**Amazon OpenSearch Service** aprovisiona todos los recursos OpenSearch del clúster y lo lanza. También detecta y reemplaza automáticamente los nodos de OpenSearch servicio defectuosos, lo que reduce la sobrecarga asociada a las infraestructuras autogestionadas. Puede escalar el clúster con una única llamada a la API o con algunos clics en la consola.” |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/appsync/latest/devguide/data-source-components.html)  | 
| Puntos de conexión HTTP | Puedes usar puntos finales HTTP como fuentes de datos. AWS AppSync puede enviar solicitudes a los puntos finales con la información relevante, como los parámetros y la carga útil. La respuesta HTTP estará expuesta al solucionador, que devolverá la respuesta final cuando finalice sus operaciones. |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/appsync/latest/devguide/data-source-components.html)  | 
| Amazon EventBridge | «EventBridge es un servicio sin servidor que utiliza eventos para conectar los componentes de la aplicación, lo que facilita la creación de aplicaciones escalables basadas en eventos. Úselo para redirigir eventos desde fuentes como aplicaciones propias, AWS servicios y software de terceros a aplicaciones de consumo de toda la organización. EventBridge proporciona una forma sencilla y coherente de incorporar, filtrar, transformar y distribuir eventos para que pueda crear nuevas aplicaciones rápidamente». |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/appsync/latest/devguide/data-source-components.html)  | 
| Bases de datos relacionales | «Amazon Relational Database Service (Amazon RDS) es un servicio web que facilita la configuración, el funcionamiento y el escalado de una base de datos relacional en la nube. AWS Proporciona una capacidad rentable y de tamaño ajustable para una base de datos relacional estándar y se ocupa de las tareas de administración de bases de datos comunes.” |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/appsync/latest/devguide/data-source-components.html)  | 
| Origen de datos none | Si no planea usar un servicio de origen de datos, puede configurarlo en none. Un origen de datos none, aunque se siga considerando explícitamente un origen de datos, no es un medio de almacenamiento. A pesar de ello, sigue siendo útil en algunos casos para la manipulación y transferencia de datos. |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/appsync/latest/devguide/data-source-components.html)  | 

**sugerencia**  
Para obtener más información sobre cómo interactúan las fuentes de datos AWS AppSync, consulta Cómo [adjuntar una fuente de datos](https://docs.aws.amazon.com//appsync/latest/devguide/attaching-a-data-source.html).

# Solucionadores
<a name="resolver-components"></a>

En las secciones anteriores, ha aprendido acerca de los componentes del esquema y el origen de datos. Ahora, debemos abordar cómo interactúan el esquema y los orígenes de datos. Todo comienza con el solucionador.

Un solucionador es una unidad de código que gestiona cómo se resolverán los datos de ese campo cuando se realice una solicitud al servicio. Los solucionadores se adjuntan a campos específicos dentro de los tipos de su esquema. Por lo general, se utilizan para implementar las operaciones de cambio de estado de las operaciones de los campos de consulta, mutación y suscripción. El solucionador procesará la solicitud del cliente y, a continuación, devolverá el resultado, que puede ser un grupo de tipos de salida, como objetos o escalares:

![\[GraphQL schema with resolvers connecting to various AWS data sources for a single endpoint.\]](http://docs.aws.amazon.com/es_es/appsync/latest/devguide/images/aws-flow-infographic.png)


## Tiempo de ejecución del solucionador
<a name="resolver-components-runtime"></a>

En AWS AppSync, primero debe especificar un tiempo de ejecución para la resolución. El tiempo de ejecución de un solucionador indica el entorno en el que este se ejecuta. También dicta el idioma en el que se escribirán los resolutores. AWS AppSync actualmente es compatible con APPSYNC\$1JS JavaScript y Velocity Template Language (VTL). Consulte [las características del JavaScript tiempo de ejecución para ver los resolutores y las funciones](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-util-reference-js.html) JavaScript o consulte la referencia de la utilidad de [plantillas de mapeo de Resolver](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-util-reference.html) para VTL.

## Estructura del solucionador
<a name="resolver-components-structure"></a>

En cuanto al código, los solucionadores se pueden estructurar de dos maneras. Hay solucionadores de **unidad** y de **canalización**.

### Solucionadores de unidad
<a name="resolver-components-unit"></a>

Un solucionador de unidad se compone de código que define un solo controlador de solicitudes y respuestas que se ejecutan con un origen de datos. El controlador de solicitudes toma un objeto de contexto como argumento y devuelve la carga de la solicitud utilizada para llamar al origen de datos. El controlador de respuestas recibe una carga útil del origen de datos con el resultado de la solicitud ejecutada. El controlador de respuestas transforma la carga útil en una respuesta de GraphQL para resolver el campo de GraphQL.

![\[GraphQL request flow showing request and response handlers interacting with a data source.\]](http://docs.aws.amazon.com/es_es/appsync/latest/devguide/images/unit-resolver-js.png)


### Solucionadores de canalización
<a name="resolver-components-pipeline"></a>

Al implementar los solucionadores de canalización, estos siguen una estructura general:
+ **Paso anterior**: cuando el cliente realiza una solicitud, los datos de esta se envían a los solucionadores de los campos de esquema que se usan (normalmente, las consultas, las mutaciones y las suscripciones). El solucionador empezará a procesar los datos de la solicitud con un controlador previo paso a paso, que permite realizar algunas operaciones de preprocesamiento antes de que los datos pasen por el solucionador.
+ **Funciones**: una vez ejecutado el paso anterior, la solicitud se pasa a la lista de funciones. La primera función de la lista se ejecuta conforme al origen de datos. Una función es un subconjunto del código de su solucionador, que contiene su propio controlador de solicitudes y respuestas. Un controlador de solicitudes toma los datos de la solicitud y realiza operaciones con el origen de datos. El controlador de respuestas procesa la respuesta del origen de datos antes de devolverla a la lista. Si hay más de una función, los datos de la solicitud se envían a la siguiente función de la lista que se ejecutará. Las funciones de la lista se ejecutan en serie en el orden definido por el desarrollador. Una vez ejecutadas todas las funciones, el resultado final se envía al paso posterior.
+ **Paso posterior**: el paso posterior es una función de controlador que permite realizar algunas operaciones finales en la respuesta de la función final antes de pasarla a la respuesta de GraphQL.

![\[GraphQL request flow diagram showing interactions between request, data sources, and response components.\]](http://docs.aws.amazon.com/es_es/appsync/latest/devguide/images/appsync-js-resolver-logic.png)


## Estructura del controlador del solucionador
<a name="resolver-components-handlers"></a>

Los controladores suelen ser funciones denominadas `Request` y `Response`:

```
export function request(ctx) {
    // Code goes here
}

export function response(ctx) {
    // Code goes here
}
```

En un solucionador de unidad, solo habrá un conjunto de estas funciones. En un solucionador de canalización, habrá un conjunto de estas funciones para el paso anterior y posterior y un conjunto adicional para cada función. Para visualizar cómo podría verse esto, revisemos un simple tipo `Query`:

```
type Query {
	helloWorld: String!
}
```

Se trata de una consulta sencilla con un campo denominado `helloWorld` de tipo `String`. Supongamos que siempre queremos que este campo devuelva la cadena “Hello World”. Para implementar este comportamiento, necesitamos añadir el solucionador a este campo. En un solucionador de unidad, podríamos añadir algo parecido a esto:

```
export function request(ctx) {
    return {}
}

export function response(ctx) {
    return "Hello World"
}
```

La `request` puede dejarse en blanco porque no vamos a solicitar ni procesar datos. También podemos suponer que nuestro origen de datos es `None`, lo que indica que este código no necesita realizar ninguna invocación. La respuesta simplemente devuelve “Hello World”. Para probar este solucionador, necesitamos realizar una solicitud utilizando el tipo de consulta:

```
query helloWorldTest {
  helloWorld
}
```

Esta es una consulta llamada `helloWorldTest` que devuelve el campo `helloWorld`. Cuando se ejecuta, el solucionador del campo `helloWorld` también ejecuta y devuelve la respuesta:

```
{
  "data": {
    "helloWorld": "Hello World"
  }
}
```

Devolver constantes como esta es lo más sencillo que puede hacer. En realidad, devolverá entradas, listas y más. Este es un ejemplo más complicado:

```
type Book {
  id: ID!
  title: String
}

type Query {
  getBooks: [Book]
}
```

Aquí devolvemos una lista de `Books`. Supongamos que utilizamos una tabla de DynamoDB para almacenar datos de libros. Nuestros controladores pueden tener un aspecto similar al siguiente:

```
/**
 * Performs a scan on the dynamodb data source
 */
export function request(ctx) {
  return { operation: 'Scan' };
}

/**
 * return a list of scanned post items
 */
export function response(ctx) {
  return ctx.result.items;
}
```

Nuestra solicitud ha utilizado una operación de escaneo integrada para buscar todas las entradas de la tabla, ha almacenado los resultados en el contexto y, a continuación, los ha transferido a la respuesta. La respuesta ha tomado los elementos resultantes y los ha devuelto dentro de ella:

```
{
  "data": {
    "getBooks": {
      "items": [
        {
          "id": "abcdefgh-1234-1234-1234-abcdefghijkl",
          "title": "book1"
        },
        {
          "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
          "title": "book2"
        },

        ...

      ]
    }
  }
}
```

## Contexto del solucionador
<a name="resolver-components-context"></a>

En un solucionador, cada paso de la cadena de controladores debe conocer el estado de los datos de los pasos anteriores. El resultado de un controlador se puede almacenar y transferir a otro como argumento. GraphQL define cuatro argumentos básicos del solucionador:


****  

| Argumentos básicos del solucionador | Description (Descripción) | 
| --- | --- | 
| obj, root, parent, etc.: | El resultado del nivel superior. | 
| args | Los argumentos proporcionados al campo en la consulta GraphQL. | 
| context | Un valor que se proporciona a todos los solucionadores y que contiene información contextual importante, como el usuario registrado actualmente o el acceso a una base de datos. | 
| info | Un valor que contiene información específica del campo relevante para la consulta actual, así como los detalles del esquema. | 

En AWS AppSync, el argumento `[context](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference-js.html)` (ctx) puede contener todos los datos mencionados anteriormente. Es un objeto que se crea por solicitud y contiene datos como las credenciales de autorización, los datos de los resultados, los errores, los metadatos de la solicitud, etc. El contexto facilita a los programadores la manipulación de los datos procedentes de otras partes de la solicitud. Vuelva a leer este fragmento de código:

```
/**
 * Performs a scan on the dynamodb data source
 */
export function request(ctx) {
  return { operation: 'Scan' };
}

/**
 * return a list of scanned post items
 */
export function response(ctx) {
  return ctx.result.items;
}
```

La solicitud recibe el contexto (ctx) como argumento; este es el estado de la solicitud. Realiza un análisis de todos los elementos de una tabla y, a continuación, vuelve a almacenar el resultado en el contexto en `result`. A continuación, el contexto se transfiere al argumento de respuesta, que accede al `result` y devuelve su contenido.

## Solicitudes y análisis
<a name="resolver-ast"></a>

Cuando realiza una consulta a su servicio GraphQL, este debe pasar por un proceso de análisis y validación antes de ejecutarse. Su solicitud se analizará y traducirá en un árbol sintáctico abstracto. El contenido del árbol se valida mediante la ejecución de varios algoritmos de validación en función de su esquema. Tras el paso de validación, se recorren y procesan los nodos del árbol. Se invoca a los solucionadores, los resultados se almacenan en el contexto y se devuelve la respuesta. Tomemos por ejemplo esta consulta:

```
query {
  Person {  //object type
    name  //scalar
    age   //scalar
  } 
}
```

Devolvemos `Person` con los campos `name` y `age`. Al ejecutar esta consulta, el árbol tendrá un aspecto parecido al siguiente:

![\[Hierarchical diagram showing query, Person, name, and age nodes connected by arrows.\]](http://docs.aws.amazon.com/es_es/appsync/latest/devguide/images/ast-1.png)


En el árbol, parece que esta solicitud buscará la raíz para la `Query` en el esquema. Dentro de la consulta, se resolverá el campo `Person`. Por los ejemplos anteriores, sabemos que podría ser una entrada del usuario, una lista de valores, etc. Lo más probable es que la `Person` esté vinculada a un tipo de objeto que contenga los campos que necesitamos (`name` y `age`). Cuando se encuentran estos dos campos secundarios, se resuelven en el orden indicado (`name` y después `age`). Cuando el árbol esté completamente resuelto, la solicitud estará completa y se devolverá al cliente.

# Propiedades adicionales de GraphQL
<a name="graphql-properties"></a>

GraphQL consta de varios principios de diseño para mantener la simplicidad y la solidez a escala.

## Declarativo
<a name="declarative-property"></a>

GraphQL es declarativo, lo que significa que el usuario describirá (dará forma) a los datos declarando únicamente los campos que desee consultar. La respuesta solo devolverá los datos de estas propiedades. Por ejemplo, esta es una operación que recupera un `Book` objeto de una tabla de DynamoDB con el valor ISBN 13 de: `id` *9780199536061*

```
{
  getBook(id: "9780199536061") {
    name
    year
    author
  }
}
```

La respuesta devolverá los campos de la carga (`name`, `year` y `author`) y nada más:

```
{
  "data": {
    "getBook": {
      "name": "Anna Karenina",
      "year": "1878",
      "author": "Leo Tolstoy",
    }
  }
}
```

Gracias a este principio de diseño, GraphQL elimina los problemas perennes de sobreprocesamiento o subprocesamiento que REST plantea en sistemas APIs complejos. Esto se traduce en una recopilación de datos más eficiente y en un mejor rendimiento de la red.

## Jerárquico
<a name="hierarchical-property"></a>

GraphQL es flexible en el sentido de que el usuario puede configurar los datos solicitados para adaptarlos a las necesidades de la aplicación. Los datos solicitados siempre siguen los tipos y la sintaxis de las propiedades definidas en la API de GraphQL. Por ejemplo, en el siguiente fragmento se muestra la `getBook` operación con un nuevo campo denominado `quotes` que devuelve todas las cadenas de comillas almacenadas y las páginas enlazadas a: `Book` *9780199536061*

```
{
  getBook(id: "9780199536061") {
    name
    year
    author
    quotes {
      description
      page
    }
  }
}
```

Al ejecutar esta consulta, se devuelve el resultado siguiente:

```
{
  "data": {
    "getBook": {
      "name": "Anna Karenina",
      "year": "1878",
      "author": "Leo Tolstoy",
      "quotes": [
         {
            "description": "The highest Petersburg society is essentially one: in it everyone knows everyone else, everyone even visits everyone else.",
            "page": 135
         },
         { 
            "description": "Happy families are all alike; every unhappy family is unhappy in its own way.",
            "page": 1
         },
         {        
            "description": "To Konstantin, the peasant was simply the chief partner in their common labor.",
            "page": 251
         }
      ]
    }
  }
}
```

Como puede ver, los campos `quotes` vinculados al libro solicitado se han devuelto como una matriz con el mismo formato descrito en nuestra consulta. Aunque no se ha mostrado aquí, GraphQL tiene la ventaja adicional de no ser exigente en cuanto a la ubicación de los datos que recupera. Los `Books` y las `quotes` podría almacenarse por separado, pero GraphQL seguirá recuperando la información mientras exista la asociación. Esto significa que su consulta puede recuperar múltiples datos independientes en una sola solicitud.

## Introspectivo
<a name="introspective-property"></a>

GraphQL se documenta automáticamente o es declarativo. Admite varias operaciones integradas que permiten a los usuarios ver los tipos y campos subyacentes dentro del esquema. Por ejemplo, este es un tipo `Foo` con un campo `date` y `description`:

```
type Foo {
	date: String
	description: String
}
```

Podríamos usar la operación `_type` para buscar los metadatos de escritura que se encuentran debajo del esquema:

```
{
  __type(name: "Foo") {
    name                   # returns the name of the type
    fields {               # returns all fields in the type
      name                 # returns the name of each field
      type {               # returns all types for each field
        name               # returns the scalar type
      }
    }
  }
}
```

Se devolverá una respuesta:

```
{
  "__type": {
    "name": "Foo",                     # The type name
    "fields": [
      {
        "name": "date",                # The date field
        "type": { "name": "String" }   # The date's type
      },
      {
        "name": "description",         # The description field
        "type": { "name": "String" }   # The description's type
      },
    ]
  }
}
```

Esta característica se puede utilizar para averiguar qué tipos y campos admite un esquema de GraphQL en particular. GraphQL admite una amplia variedad de estas operaciones introspectivas. Para obtener más información, consulte [Introspección](https://graphql.org/learn/introspection/).

## Tipado fuerte
<a name="strong-typing-property"></a>

GraphQL admite el tipado fuerte a través de su sistema de tipos y campos. Cuando defina algo en su esquema, debe tener un tipo que pueda validarse antes de su tiempo de ejecución. También debe seguir la especificación de sintaxis de GraphQL. Este concepto no es diferente de la programación en otros lenguajes. Por ejemplo, este es el tipo `Foo` de antes:

```
type Foo {
	date: String
	description: String
}
```

Podemos ver que ese `Foo` es el objeto que se va a crear. Dentro de una instancia de `Foo`, habrá un campo `date` y otro `description`, ambos del tipo primitivo (escalar) `String`. Sintácticamente, vemos que `Foo` se ha declarado y que sus campos están dentro de su ámbito. Esta combinación de comprobación de tipos y sintaxis lógica garantiza que la API de GraphQL sea concisa y evidente por sí misma. La especificación de tipos y sintaxis de GraphQL se encuentra [aquí](https://spec.graphql.org/).