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.
El esquema GraphQL es la base de cualquier implementación de servidor GraphQL. Cada API de GraphQL se define mediante un solo esquema que contiene tipos y campos que describen cómo se rellenarán los datos de las solicitudes. Los datos que fluyen a través de la API y las operaciones realizadas deben validarse con respecto al esquema.
En general, el sistema de tipos de GraphQL
AWS AppSync permite definir y configurar esquemas de GraphQL. En la siguiente sección se describe cómo crear esquemas de GraphQL desde cero utilizando AWS AppSync los servicios.
Estructuración de un esquema de GraphQL
sugerencia
Recomendamos consultar la sección Esquemas antes de continuar.
GraphQL es una poderosa herramienta para implementar servicios de API. Según el sitio web de GraphQL
«GraphQL es un lenguaje de consulta APIs y un entorno de ejecución para completar esas consultas con los datos existentes. GraphQL proporciona una descripción completa y comprensible de los datos de tu API, da a los clientes la posibilidad de pedir exactamente lo que necesitan y nada más, facilita la evolución APIs con el tiempo y habilita potentes herramientas para desarrolladores. »
Esta sección cubre la primera parte de la implementación de GraphQL, el esquema. Siguiendo la cita anterior, un esquema cumple la función de “proporcionar una descripción completa y comprensible de los datos de su API”. En otras palabras, un esquema GraphQL es una representación textual de los datos, las operaciones y las relaciones entre ellos de su servicio. El esquema se considera el punto de entrada principal para la implementación del servicio GraphQL. Como era de esperar, suele ser una de las primeras cosas que hace en su proyecto. Recomendamos consultar la sección Esquemas antes de continuar.
Para citar la sección Esquemas, los esquemas de GraphQL se escriben en el lenguaje de definición de esquema (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 estructuren los datos en una operación de solicitud/respuesta. Esto permite a los desarrolladores predecir lo que se va a devolver sin saber cómo se implementa el backend del servicio.
Los esquemas más simples contendrán tres categorías de datos diferentes:
-
Raíces del esquema: las raíces definen los puntos de entrada de su esquema. Señala los campos que realizarán alguna operación con los datos, como añadir, eliminar o modificar algo.
-
Tipos: son tipos básicos que se utilizan para representar la forma de los datos. Casi se puede pensar en ellos como objetos o representaciones abstractas de algo con características definidas. Por ejemplo, podría crear un objeto
Person
que represente a una persona en una base de datos. Las características de cada persona se definirán dentro dePerson
como campos. Pueden ser cualquier cosa, como el nombre, la edad, el trabajo, la dirección, etc. de la persona. -
Tipos de objetos especiales: son los tipos que definen el comportamiento de las operaciones del esquema. Cada tipo de objeto especial se define una vez por cada esquema. Primero se colocan en la raíz del esquema y, a continuación, se definen en el cuerpo del esquema. Cada campo de un tipo de objeto especial define una sola operación que debe implementar el solucionador.
Para poner esto en perspectiva, imagine que está creando un servicio que almacena autores y los libros que han escrito. Cada autor tiene un nombre y una serie de libros que han escrito. Cada libro tiene un nombre y una lista de autores asociados. También queremos poder añadir o recuperar libros y autores. Una representación UML simple de esta relación puede tener este aspecto:

En GraphQL, las entidades Author
y Book
representan dos tipos de objetos diferentes en el esquema:
type Author {
}
type Book {
}
Author
contiene authorName
y Books
, mientras que Book
contiene bookName
y Authors
. Estos se pueden representar como los campos incluidos en el ámbito de sus tipos:
type Author {
authorName: String
Books: [Book]
}
type Book {
bookName: String
Authors: [Author]
}
Como puede ver, las representaciones de tipos se parecen mucho al diagrama. Sin embargo, los métodos son algo más complicados. Se colocarán como campo en uno de algunos tipos de objetos especiales. La categorización de los objetos especiales depende de su comportamiento. GraphQL contiene tres tipos de objetos especiales fundamentales: consultas, mutaciones y suscripciones. Para obtener más información sobre los objetos, consulte Objetos especiales.
Como tanto getAuthor
como getBook
solicitan datos, se colocarán en un tipo de objeto especial de Query
:
type Author {
authorName: String
Books: [Book]
}
type Book {
bookName: String
Authors: [Author]
}
type Query {
getAuthor(authorName: String): Author
getBook(bookName: String): Book
}
Las operaciones están vinculadas a la consulta, que a su vez está vinculada al esquema. Cuando se añade una raíz de esquema, se define el tipo de objeto especial (en este caso, Query
) como uno de los puntos de entrada. Esto se puede hacer con la palabra clave schema
:
schema {
query: Query
}
type Author {
authorName: String
Books: [Book]
}
type Book {
bookName: String
Authors: [Author]
}
type Query {
getAuthor(authorName: String): Author
getBook(bookName: String): Book
}
Si nos fijamos en los dos últimos métodos, addAuthor
y addBook
añaden datos a la base de datos, por lo que se definirán en un tipo de objeto Mutation
especial. Sin embargo, por la página Tipos, también sabemos que no se permiten entradas que hagan referencia directamente a objetos porque son estrictamente tipos de salida. En este caso, no podemos usar Author
ni Book
, por lo que necesitamos crear un tipo de entrada con los mismos campos. En este ejemplo, añadimos AuthorInput
yBookInput
, que aceptan los mismos campos de sus tipos correspondientes. A continuación, creamos nuestra mutación usando las entradas como parámetros:
schema {
query: Query
mutation: Mutation
}
type Author {
authorName: String
Books: [Book]
}
input AuthorInput {
authorName: String
Books: [BookInput]
}
type Book {
bookName: String
Authors: [Author]
}
input BookInput {
bookName: String
Authors: [AuthorInput]
}
type Query {
getAuthor(authorName: String): Author
getBook(bookName: String): Book
}
type Mutation {
addAuthor(input: [BookInput]): Author
addBook(input: [AuthorInput]): Book
}
Repasemos lo que acabamos de hacer:
-
Hemos creado un esquema con los tipos
Book
yAuthor
para representar nuestras entidades. -
Hemos añadido los campos que contienen las características de nuestras entidades.
-
Hemos añadido una consulta para recuperar esta información de la base de datos.
-
Hemos añadido una mutación para manipular los datos de la base de datos.
-
Hemos añadido tipos de entrada para reemplazar los parámetros de nuestro objeto en la mutación y cumplir con las reglas de GraphQL.
-
Hemos añadido la consulta y la mutación a nuestro esquema de raíz para que la implementación de GraphQL entienda la ubicación del tipo de raíz.
Como puede ver, el proceso de creación de un esquema requiere muchos conceptos del modelado de datos (especialmente del modelado de bases de datos) en general. Puede considerarse que el esquema se ajusta a la forma de los datos de la fuente. También sirve como modelo que el solucionador implementará. En las siguientes secciones, aprenderá a crear un esquema utilizando varias herramientas y servicios AWS respaldados por ellos.
nota
Los ejemplos de las secciones siguientes no están pensados para ejecutarse en una aplicación real. Con ellos solo se pretende mostrar los comandos para que pueda crear sus propias aplicaciones.
Creación de esquemas
El esquema estará en un archivo llamadoschema.graphql
. AWS AppSync permite a los usuarios crear nuevos esquemas para su APIs GraphQL utilizando varios métodos. En este ejemplo, crearemos una API en blanco junto con un esquema en blanco.
-
Inicia sesión en la consola AWS Management Console y ábrelaAppSync.
-
En el Panel, elija Crear API.
-
En Opciones de API, selecciona GraphQL APIs, Diseña desde cero y, a continuación, Siguiente.
-
En Nombre de la API, cambie el nombre previamente cumplimentado por el que su aplicación necesite.
-
Para obtener los detalles de contacto, puede introducir un punto de contacto para identificar al administrador de la API. Se trata de un campo opcional.
-
En Configuración de API privada, puede habilitar las características de API privadas. Solo se puede acceder a una API privada desde un punto de conexión de VPC (VPCE) configurado. Para obtener más información, consulta Privado APIs.
No le recomendamos habilitar esta característica para este ejemplo. Seleccione Siguiente después de revisar sus entradas.
-
-
En Crear un tipo de GraphQL, puede elegir crear una tabla de DynamoDB para utilizarla como origen de datos u omitir este paso y hacerlo más adelante.
Para este ejemplo, elija Crear recursos de GraphQL más adelante. Crearemos un recurso en una sección aparte.
-
Revise la información indicada y, a continuación, seleccione Crear API.
-
-
Estará en el panel de control de tu API específica. Lo notará porque el nombre de la API aparecerá en la parte superior del panel de control. Si este no es el caso, puedes seleccionarla APIsen la barra lateral y, a continuación, elegir tu API en el APIs panel de control.
-
En la barra lateral situada debajo del nombre de la API, seleccione Esquema.
-
-
En el Editor de esquemas, puede configurar su archivo
schema.graphql
. Puede estar vacío o lleno de tipos generados a partir de un modelo. A la derecha, tiene la sección Solucionadores para asociar solucionadores a los campos del esquema. En esta sección no nos fijaremos en los solucionadores.
Adición de tipos a los esquemas
Ahora que ha añadido su esquema, puede empezar a agregar los tipos tanto de entrada como de salida. Tenga en cuenta que los tipos que aparecen aquí no deben usarse en código real; son solo ejemplos que le ayudarán a entender el proceso.
En primer lugar, crearemos un tipo de objeto. En el código real, no es necesario empezar con estos tipos. Puede crear cualquier tipo que desee en cualquier momento siempre que siga las reglas y la sintaxis de GraphQL.
nota
Las siguientes secciones usarán el editor de esquemas, así que manténgalo abierto.
-
Puede crear un tipo de objeto utilizando la palabra clave
type
junto con el nombre del tipo:type
Type_Name_Goes_Here
{}Dentro del ámbito del tipo, puede añadir campos que representen las características del objeto:
type
Type_Name_Goes_Here
{ # Add fields here }A continuación se muestra un ejemplo:
type
Obj_Type_1
{id: ID! title: String date: AWSDateTime
}nota
En este paso, hemos añadido un tipo de objeto genérico con un campo
id
obligatorio almacenado comoID
, un campotitle
almacenado comoString
y un campodate
almacenado comoAWSDateTime
. Para ver una lista de tipos y campos, y lo que hacen, consulte Esquemas. Para ver una lista de escalares y lo que hacen, consulte Referencia de tipos.
El tipo de objetoAWSDateTime
además de los escalares básicos de GraphQL. Además, todos los campos que terminen con un signo de exclamación son obligatorios.
El tipo escalar ID
en concreto es un identificador exclusivo que puede ser String
o Int
. Puede controlarlos en el código de resolución para la asignación automática.
Existen similitudes entre los tipos de objetos especiales, como Query
y los tipos de objetos “normales”, como en el ejemplo anterior, en el sentido de que ambos utilizan la palabra clave type
y se consideran objetos. Sin embargo, en el caso de los tipos de objetos especiales (Query
, Mutation
, y Subscription
), su comportamiento es muy diferente, ya que se exponen como puntos de entrada a la API. También se centran más en dar forma a las operaciones que a los datos. Para obtener más información, consulte la sección sobre tipos de consultas y mutaciones
En cuanto a los tipos de objetos especiales, el siguiente paso podría ser añadir uno o más para realizar operaciones con los datos con forma. En un escenario real, cada esquema de GraphQL debe tener al menos un tipo de consulta raíz para solicitar datos. Puede considerar la consulta como uno de los puntos de entrada (o puntos de conexión) de su servidor GraphQL. Añadamos una consulta como ejemplo.
-
Para crear una consulta, basta con añadirla al archivo de esquema como cualquier otro tipo. Una consulta requeriría un tipo
Query
y una entrada en la raíz de la manera siguiente:schema { query:
Name_of_Query
} typeName_of_Query
{ # Add field operation here }Ten en cuenta que
Name_of_Query
en un entorno de producción simplemente se llamaráQuery
en la mayoría de los casos. Se recomienda mantenerlo en este valor. Dentro del tipo de consulta, puede añadir campos. Cada campo realizará una operación en la solicitud. El resultado es que la mayoría de estos campos, si no todos, se asociarán a un solucionador. Sin embargo, en esta sección o abordaremos eso. En cuanto al formato de la operación de campo, podría ser como este:Name_of_Query(params): Return_Type
# version with paramsName_of_Query: Return_Type
# version without paramsA continuación se muestra un ejemplo:
schema { query: Query } type Query {
getObj: [Obj_Type_1]
} type Obj_Type_1 { id: ID! title: String date: AWSDateTime }nota
En este paso, agregamos un tipo
Query
y lo definimos en nuestra raíz deschema
. Nuestro tipoQuery
definió un campogetObj
que devuelve una lista de objetosObj_Type_1
. Tenga en cuenta queObj_Type_1
es el objeto del paso anterior. En el código de producción, sus operaciones de campo normalmente funcionarán con datos moldeados por objetos comoObj_Type_1
. Además, campos comogetObj
suelen tener un solucionador para ejecutar la lógica empresarial. Este tema se tratará en otra sección.Como nota adicional, agrega AWS AppSync automáticamente una raíz del esquema durante las exportaciones, por lo que técnicamente no es necesario agregarla directamente al esquema. Nuestro servicio procesará automáticamente los esquemas duplicados. Lo añadimos aquí como práctica recomendada.
Ahora ya ha visto un ejemplo de creación tanto de objetos como de objetos especiales (consultas). También ha visto cómo se pueden interconectar para describir datos y operaciones. Puede tener esquemas con solo la descripción de los datos y una o más consultas. Sin embargo, nos gustaría añadir otra operación para añadir datos al origen de datos. Vamos a añadir otro tipo de objeto especial denominado Mutation
que modifica los datos.
-
Una mutación se llamará
Mutation
. Como enQuery
, las operaciones de campo de dentro deMutation
describirán una operación y se asociarán a un solucionador. Tenga en cuenta también que debemos definirla en la raízschema
porque es un tipo de objeto especial. Aquí tiene un ejemplo de mutación:schema {
mutation: Name_of_Mutation
} typeName_of_Mutation
{ # Add field operation here }Una mutación típica aparecerá en la raíz en forma de consulta. La mutación se define con la
type
palabra clave junto con el nombre.Name_of_Mutation
normalmente se llamará asíMutation
, por lo que recomendamos mantenerlo así. Cada campo realizará también una operación. En cuanto al formato de la operación de campo, podría ser como este:Name_of_Mutation(params): Return_Type # version with params Name_of_Mutation: Return_Type # version without params
A continuación se muestra un ejemplo:
schema { query: Query
mutation: Mutation
} type Obj_Type_1 { id: ID! title: String date: AWSDateTime } type Query { getObj: [Obj_Type_1] } type Mutation {addObj(id: ID!, title: String, date: AWSDateTime): Obj_Type_1
}nota
En este paso, hemos añadido un tipo
Mutation
con un campoaddObj
. Vamos a resumir lo que hace este campo:addObj(id: ID!, title: String, date: AWSDateTime): Obj_Type_1
addObj
está utilizando el objetoObj_Type_1
para realizar una operación. Esto es evidente debido a los campos, pero la sintaxis lo demuestra en el tipo de retorno: Obj_Type_1
. Dentro deaddObj
, acepta los camposid
,title
ydate
del objetoObj_Type_1
como parámetros. Como puede ver, es muy parecido a la declaración de un método. Sin embargo, aún no hemos descrito el comportamiento de nuestro método. Como se ha afirmado antes, el esquema solo está ahí para definir cuáles serán los datos y las operaciones, no cómo funcionan. La lógica empresarial real se implementará más adelante, cuando creemos nuestros primeros solucionadores.Una vez que haya terminado con su esquema, hay una opción para exportarlo como archivo
schema.graphql
. En el editor de esquemas, puede elegir Exportar esquema para descargar el archivo en un formato compatible.Como nota adicional, agrega AWS AppSync automáticamente una raíz del esquema durante las exportaciones, por lo que técnicamente no es necesario agregarla directamente al esquema. Nuestro servicio procesará automáticamente los esquemas duplicados. Lo añadimos aquí como práctica recomendada.
Consideraciones opcionales: uso de enumeraciones como estados
Llegados a este punto, ya sabe cómo hacer un esquema básico. Sin embargo, hay muchas cosas que se pueden añadir para aumentar la funcionalidad del esquema. Una característica común que se encuentra en las aplicaciones es el uso de enumeraciones como estados. Puede usar una enumeración para forzar la elección de un valor específico de un conjunto de valores cuando se invoque. Esto es adecuado para cosas que sabe que no van a cambiar drásticamente durante largos períodos de tiempo. Hipotéticamente, podríamos añadir una enumeración que devuelva el código de estado o cadena en la respuesta.
Como ejemplo, supongamos que vamos a crear una aplicación de redes sociales que almacene los datos de las publicaciones de un usuario en el backend. Nuestro esquema contiene un tipo Post
que representa los datos de una publicación individual:
type Post {
id: ID!
title: String
date: AWSDateTime
poststatus: PostStatus
}
Nuestra Post
contendrá un id
único, un título de title
, una date
de publicación y una enumeración denominada PostStatus
que representa el estado de la publicación a medida que la aplicación la procesa. Para nuestras operaciones, tendremos una consulta que devolverá todos los datos de la publicación:
type Query {
getPosts: [Post]
}
También tendremos una mutación que añade publicaciones al origen de datos:
type Mutation {
addPost(id: ID!, title: String, date: AWSDateTime, poststatus: PostStatus): Post
}
Si observamos nuestro esquema, la enumeración PostStatus
podría tener varios estados. Tal vez que queramos que los tres estados básicos se llamen success
(publicación procesada correctamente), pending
(publicación procesada) y error
(publicación que no se puede procesar). Para añadir la enumeración, podemos hacer lo siguiente:
enum PostStatus {
success
pending
error
}
El esquema completo podría ser como este:
schema {
query: Query
mutation: Mutation
}
type Post {
id: ID!
title: String
date: AWSDateTime
poststatus: PostStatus
}
type Mutation {
addPost(id: ID!, title: String, date: AWSDateTime, poststatus: PostStatus): Post
}
type Query {
getPosts: [Post]
}
enum PostStatus {
success
pending
error
}
Si un usuario añade una Post
en la aplicación, se llamará a la operación addPost
para procesar esos datos. A medida que el solucionador adjunto a addPost
procese los datos, actualizará continuamente poststatus
con el estado de la operación. Cuando se consulte, Post
contendrá el estado final de los datos. Tenga en cuenta que solo estamos describiendo cómo queremos que funcionen los datos en el esquema. Estamos haciendo muchas suposiciones sobre la implementación de nuestros solucionadores, que implementarán la lógica empresarial real para gestionar los datos a fin de cumplir con la solicitud.
Consideraciones opcionales: suscripciones
Las suscripciones en AWS AppSync se invocan como respuesta a una mutación. Se puede configurar con un tipo Subscription
y una directiva @aws_subscribe()
en el esquema para indicar qué mutaciones invocan una o varias suscripciones. Para obtener más información sobre cómo configurar suscripciones, consulte Datos en tiempo real.
Consideraciones opcionales: relaciones y paginación
Supongamos que tiene un millón de Posts
almacenadas en una tabla de DynamoDB y desea devolver algunos de esos datos. Sin embargo, la consulta de ejemplo anterior solo devuelve todas las publicaciones. No le interesa buscarlas todas cada vez que realice una solicitud. Más bien, le interesa paginarlas
-
En el campo
getPosts
, añada dos argumentos de entrada:nextToken
(iterador) ylimit
(límite de iteración). -
Añada un nuevo tipo de
PostIterator
que contenga los camposPosts
(recupera la lista de objetosPost
) ynextToken
(iterador). -
Modifique
getPosts
para que devuelvaPostIterator
y no una lista de objetos dePost
.
schema {
query: Query
mutation: Mutation
}
type Post {
id: ID!
title: String
date: AWSDateTime
poststatus: PostStatus
}
type Mutation {
addPost(id: ID!, title: String, date: AWSDateTime, poststatus: PostStatus): Post
}
type Query {
getPosts(limit: Int, nextToken: String): PostIterator
}
enum PostStatus {
success
pending
error
}
type PostIterator {
posts: [Post]
nextToken: String
}
El tipo PostIterator
le permite devolver una parte de la lista de objetos de Post
y un nextToken
para obtener la parte siguiente. Dentro de PostIterator
, hay una lista de elementos de Post
([Post]
) que se devuelve con un token de paginación (nextToken
). En AWS AppSync, se conectaría a Amazon DynamoDB a través de un solucionador y se generaría automáticamente como un token cifrado. Así se convierte el valor del argumento limit
en el parámetro maxResults
y el argumento nextToken
en el parámetro exclusiveStartKey
. Para ver ejemplos y los ejemplos de plantillas integradas en la AWS AppSync consola, consulte Resolver reference () JavaScript.