

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

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

O esquema do GraphQL é a base de uma API GraphQL. Ele serve como o esquema que define a forma dos dados. Também é um contrato entre seu cliente e servidor que define como seus dados serão recuperados e and/or modificados.

Os esquemas do GraphQL são escritos em *Schema Definition Language* (SDL). O SDL é composto por tipos e campos com uma estrutura estabelecida:
+ **Tipos**: os tipos são como o GraphQL define a forma e o comportamento dos dados. O GraphQL é compatível com uma infinidade de tipos que serão explicados ainda nesta seção. Cada um dos tipos definidos em seu esquema terá um escopo próprio. Dentro do escopo, um ou mais campos vão apresentar um valor ou lógica que será usada em seu serviço do GraphQL. Os tipos têm muitas funções diferentes, sendo as mais comuns objetos ou escalares (tipos de valores primitivos).
+ **Campos**: os campos existem dentro do escopo de um tipo e contêm o valor solicitado do serviço do GraphQL. Eles são muito semelhantes às variáveis de outras linguagens de programação. A forma dos dados que você define em seus campos determinará como os dados são estruturados em uma request/response operação. Isso permite que os desenvolvedores prevejam o que será retornado sem saber como o back-end do serviço é implementado.

Para visualizar a aparência de um esquema, vamos revisar o conteúdo de um esquema simples do GraphQL. No código de produção, seu esquema normalmente ficará em um arquivo chamado `schema.graphql` ou `schema.json`. Vamos supor que estamos examinando um projeto que implementa um serviço GraphQL. Este projeto está armazenando dados de funcionários da empresa, e o arquivo `schema.graphql` está sendo usado para recuperar dados de funcionários e adicionar novos funcionários a um banco de dados. O código pode ser semelhante a:

------
#### [ 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 há três tipos definidos no esquema: `Person``Query` e `Mutation`. Observando `Person`, podemos supor que esse é o esquema de uma instância de um funcionário da empresa, o que faria desse tipo um objeto. Dentro de seu escopo, vemos `id`, `name` e `age`. Esses são os campos que definem as propriedades de uma `Person`. Isso significa que nossa fonte de dados armazena cada `name` de `Person` como um tipo `String` escalar (primitivo) e `age` como um tipo `Int` escalar (primitivo). O `id` atua como um identificador especial e exclusivo para cada `Person`. Ele também é um valor obrigatório, conforme indicado pelo símbolo `!`.

Os próximos dois tipos de objetos se comportam de forma diferente. O GraphQL reserva algumas palavras-chave para tipos de objetos especiais que definem como os dados serão preenchidos no esquema. Um tipo `Query` recupera dados da fonte. Em nosso exemplo, nossa consulta pode recuperar objetos `Person` de um banco de dados. Isso pode lembrá-lo das `GET` operações na RESTful terminologia. Uma `Mutation` modifica os dados. Em nosso exemplo, nossa mutação pode adicionar mais objetos `Person` ao banco de dados. Isso pode soar familiar devido às operações de alteração de estado, como `PUT` ou `POST`. Os comportamentos de todos os tipos de objetos especiais serão explicados posteriormente nesta seção.

Vamos supor que `Query` em nosso exemplo recupere algo do banco de dados. Se olharmos para os campos de `Query`, veremos um campo chamado `people`. Seu valor de campo é `[Person]`. Isso significa que queremos recuperar alguma instância de `Person` no banco de dados. No entanto, a adição de colchetes significa que queremos retornar uma lista de todas as instâncias de `Person` e não apenas uma específica.

O tipo `Mutation` é responsável por realizar operações de mudança de estado, como modificação de dados. Uma mutação é responsável por realizar alguma operação de alteração de estado na fonte de dados. Em nosso exemplo, nossa mutação contém uma operação chamada `addPerson` que adiciona um novo objeto `Person` ao banco de dados. A mutação usa a `Person` e espera uma entrada para os campos `id`, `name` e `age`.

Neste ponto, você pode estar se perguntando como funcionam operações como `addPerson` sem uma implementação de código, já que ela supostamente executa algum comportamento e se parece muito com uma função, com um nome e parâmetros de função. Neste momento, isso não funcionaria porque um esquema serve apenas como declaração. Para implementar o comportamento de `addPerson`, teríamos que adicionar um resolvedor a ele. Um resolvedor é uma unidade de código que é executada sempre que seu campo associado (nesse caso, a operação `addPerson`) é chamado. Se você quiser usar uma operação, precisará adicionar a implementação do resolvedor em algum momento. De certa forma, você pode pensar na operação do esquema como a declaração da função e no resolvedor como a definição. Os resolvedores serão explicados em uma seção diferente.

Este exemplo mostra apenas as formas mais simples de um esquema manipular dados. Você cria aplicativos complexos, robustos e escaláveis aproveitando os atributos do GraphQL e AWS AppSync. Na próxima seção, definiremos todos os diferentes tipos e comportamentos de campo que você pode utilizar em seu esquema.

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

O GraphQL é compatível com muitos tipos diferentes. Como você viu na seção anterior, os tipos definem a forma ou o comportamento dos seus dados. Eles são os blocos de construção fundamentais de um esquema do GraphQL. 

Os tipos podem ser categorizados em entradas e saídas. As entradas são tipos que podem ser transmitidos como argumento para os tipos de objetos especiais (`Query`, `Mutation`, etc.), enquanto os tipos de saída são usados estritamente para armazenar e retornar dados. Uma lista de tipos e suas categorizações estão listadas abaixo:
+ **Objetos**: um objeto contém campos que descrevem uma entidade. Por exemplo, um objeto pode ser algo como um `book` com campos descrevendo suas características, como `authorName`, `publishingYear`, etc. Eles são estritamente tipos de saída.
+ **Escalares**: esses são tipos primitivos como int, string, etc. Normalmente, eles são atribuídos a campos. Usando o campo `authorName` como exemplo, ele pode ser atribuído ao escalar `String` para armazenar um nome como “John Smith”. Escalares podem ser do tipo de entrada e saída.
+ **Entradas**: as entradas permitem que você transmita um grupo de campos como argumento. Elas são estruturadas de forma muito semelhante aos objetos, mas podem ser passadas como argumentos para objetos especiais. As entradas permitem que você defina escalares, enums e outras entradas em seu escopo. As entradas só podem ser tipos de entrada.
+ **Objetos especiais**: objetos especiais realizam operações de mudança de estado e fazem a maior parte do trabalho pesado do serviço. Há três tipos de objetos especiais: consulta, mutação e assinatura. As consultas normalmente buscam dados; as mutações manipulam dados; as assinaturas abrem e mantêm uma conexão bidirecional entre clientes e servidores para comunicação constante. Objetos especiais não são de entrada nem saída, dada sua funcionalidade.
+ **Enums**: enums são listas predefinidas de valores legais. Se você chamar um enum, seus valores só poderão ser definidos em seu escopo. Por exemplo, se você tivesse uma enumeração chamada `trafficLights` representando uma lista de sinais de trânsito, ela poderia ter valores como `redLight` e `greenLight`, mas não `purpleLight`. Um semáforo real terá um limite de sinais, então você pode usar o enum para defini-los e forçá-los a serem os únicos valores legais ao fazer referência a `trafficLight`. Escalares podem ser do tipo de entrada e saída.
+ **Uniões/interfaces**: as uniões permitem que você retorne uma ou mais coisas em uma solicitação, dependendo dos dados solicitados pelo cliente. Por exemplo, se você tivesse um tipo `Book` com um `title` campo e um tipo `Author` com um campo `name`, você poderia criar uma união entre os dois tipos. Se seu cliente quisesse consultar um banco de dados para a frase “Júlio César”, a união poderia retornar *Júlio César* (a peça de William Shakespeare) do `title` `Book` e *Júlio César* (o autor de *Commentarii de Bello Gallico*) do `name` `Author`. As uniões só podem ser tipos de saída.

  As interfaces são conjuntos de campos que os objetos devem implementar. Isso é um pouco semelhante às interfaces em linguagens de programação como Java, nas quais você precisa implementar os campos definidos na interface. Por exemplo, digamos que você tenha criado uma interface chamada `Book` que continha um campo `title`. Digamos que você tenha criado posteriormente um tipo chamado `Novel` que implementou `Book`. Seu `Novel` teria que incluir um campo `title`. No entanto, seu `Novel` também pode incluir outros campos que não estão na interface, como `pageCount` ou `ISBN`. As interfaces só podem ser tipos de saída.

As seções a seguir explicarão como cada tipo funciona no GraphQL.

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

Os objetos do GraphQL são o tipo principal que você verá no código de produção. No GraphQL, você pode pensar em um objeto como um agrupamento de campos diferentes (semelhantes às variáveis em outras linguagens), e cada campo é definido por um tipo (normalmente um escalar ou outro objeto) que pode conter um valor. Os objetos representam uma unidade de dados que pode ser retrieved/manipulated da implementação do seu serviço.

Os tipos de objetos são declarados usando a palavra-chave `Type`. Vamos modificar um pouco nosso exemplo de esquema:

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

type Occupation {
  title: String
}
```

Os tipos de objeto aqui são `Person` e `Occupation`. Cada objeto tem seus próprios campos com seus próprios tipos. Um atributo do GraphQL é a capacidade de definir campos como outros tipos. Você pode ver que o campo `occupation` em `Person` contém um tipo de objeto `Occupation`. Podemos fazer essa associação porque o GraphQL está apenas descrevendo os dados e não a implementação do serviço.

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

Os escalares são tipos essencialmente primitivos que contêm valores. Em AWS AppSync, há dois tipos de escalares: os escalares e AWS AppSync escalares padrão do GraphQL. Os escalares costumam ser usados para armazenar valores de campo em tipos de objetos. Os tipos padrão do GraphQL incluem `Int`, `Float`, `String`,`Boolean` e `ID`. Vamos usar o exemplo anterior novamente:

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

type Occupation {
  title: String
}
```

Destacando os campos `name` e `title` e, ambos possuem um escalar `String`, e `Name` poderia retornar um valor de string como "`John Smith`" e o título poderia retornar algo como "`firefighter`”. Algumas implementações do GraphQL também oferecem suporte a escalares personalizados usando a palavra-chave`Scalar` e implementando o comportamento do tipo. No entanto, atualmente o AWS AppSync **não oferece suporte** a escalares personalizados. Para obter uma lista de escalares, consulte [Tipos de escalares](https://docs.aws.amazon.com//appsync/latest/devguide/scalars.html) no AWS AppSync.

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

Devido ao conceito de tipos de entrada e saída, existem certas restrições ao transmitir argumentos. Os tipos que normalmente precisam ser transmitidos, principalmente objetos, são restritos. Você pode usar o tipo de entrada para ignorar essa regra. As entradas são tipos que contêm escalares, enums e outros tipos de entrada.

As entradas são definidas usando a palavra-chave `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 você pode ver, podemos ter entradas separadas que imitam o tipo original. Essas entradas geralmente serão usadas em suas operações de campo da seguinte forma:

```
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 como ainda estamos transmitindo `occupationInput` em vez de `Occupation` para criar uma `Person`. 

Este é apenas um dos cenários para entradas. Elas não precisam copiar objetos 1:1 e, no código de produção, você provavelmente não as usará dessa forma. É uma prática recomendada aproveitar os esquemas do GraphQL definindo somente o que você precisa inserir como argumentos.

Além disso, as mesmas entradas podem ser usadas em várias operações, mas não recomendamos fazer isso. Preferencialmente, cada operação deve conter sua própria cópia exclusiva das entradas, caso os requisitos do esquema mudem.

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

O GraphQL reserva algumas palavras-chave para objetos especiais que definem parte da lógica de negócios de como seu esquema processará os dados. retrieve/manipulate No máximo, pode haver uma de cada uma dessas palavras-chave em um esquema. Eles atuam como pontos de entrada para todos os dados solicitados que seus clientes executam no seu serviço GraphQL. 

Objetos especiais também são definidos usando a palavra-chave `type`. Embora sejam usados de forma diferente dos tipos de objetos comuns, sua implementação é muito semelhante.

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

As consultas são muito semelhantes às operações `GET`, pois realizam uma busca somente para leitura para obter dados da sua fonte. No GraphQL, a `Query` define todos os pontos de entrada para clientes que fazem solicitações em seu servidor. Sempre haverá um `Query` em sua implementação do GraphQL.

Aqui estão os tipos `Query` de objetos modificados que usamos em nosso exemplo de esquema anterior:

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

Nosso `Query` contém um campo chamado `people` que retorna uma lista de instâncias `Person` da fonte de dados. Digamos que precisemos mudar o comportamento do nosso aplicativo e agora precisamos retornar uma lista somente das instâncias de `Occupation` para algum propósito separado. Poderíamos simplesmente adicioná-lo à consulta:

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

No GraphQL, podemos tratar nossa consulta como a única fonte de solicitações. Como você pode ver, isso é potencialmente muito mais simples do que RESTful implementações que podem usar endpoints diferentes para alcançar a mesma coisa (`.../api/1/people`e). `.../api/1/occupations`

Supondo que tenhamos uma implementação de resolvedor para essa consulta, agora podemos realizar uma consulta real. Embora o tipo `Query` exista, precisamos chamá-lo explicitamente para que ele seja executado no código do aplicativo. Isso pode ser feito usando a palavra-chave `query`:

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

Como você pode ver, essa consulta é chamada `getItems` e retorna `people` (uma lista de objetos `Person`) e `occupations` (uma lista de objetos `Occupation`). Em `people`, estamos retornando somente o campo `name` de cada `Person`, enquanto retornamos o campo `title` de cada `Occupation`. A resposta pode ser semelhante a:

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

O exemplo de resposta mostra como os dados seguem a forma da consulta. Cada entrada recuperada é listada dentro do escopo do campo. `people` e `occupations` estão retornando itens como listas separadas. Embora isso seja útil, talvez seja mais conveniente modificar a consulta para retornar uma lista dos nomes e ocupações das pessoas:

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

Essa é uma modificação legal porque nosso tipo `Person` contém um campo `occupation` de tipo `Occupation`. Quando listados no escopo de `people`, retornamos cada um `name` de `Person` junto com os `Occupation` associados por `title`. A resposta pode ser semelhante a:

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

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

As mutações são semelhantes às operações de alteração de estado, como ou `PUT` `POST`. Eles realizam uma operação de gravação para modificar os dados na fonte e, em seguida, buscam a resposta. Eles definem seus pontos de entrada para solicitações de modificação de dados. Diferentemente das consultas, uma mutação pode ou não ser incluída no esquema, dependendo das necessidades do projeto. Veja a mutação do exemplo do esquema:

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

O campo `addPerson` representa um ponto de entrada que adiciona uma `Person` à fonte de dados. `addPerson` é o nome do campo; `id`,`name` e `age` são os parâmetros; e `Person` é o tipo de retorno. Relembrando o tipo `Person`:

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

Adicionamos o campo `occupation`. No entanto, não podemos definir esse campo como `Occupation` diretamente porque os objetos não podem ser passados como argumentos; eles são estritamente tipos de saída. Em vez disso, devemos passar uma entrada com os mesmos campos de um argumento:

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

 Também podemos atualizar facilmente nosso `addPerson` para incluir isso como um parâmetro ao criar novas instâncias de `Person`:

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

Veja o esquema atualizado:

```
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 que `occupation` passará o campo `title` de `occupationInput` para concluir a criação do objeto `Person` em vez do `Occupation` original. Supondo que tenhamos uma implementação de resolvedor para `addPerson`, agora podemos realizar uma mutação real. Embora o tipo `Mutation` exista, precisamos chamá-lo explicitamente para que ele seja executado no código do aplicativo. Isso pode ser feito usando a palavra-chave `mutation`:

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

Essa mutação é chamada de `createPerson`, e `addPerson` é a operação. Para criar uma nova `Person`, podemos inserir os argumentos para `id`, `name`, `age` e `occupation`. No escopo de `addPerson`, também podemos ver outros campos como `name`, `age`, etc. Esta é sua resposta; esses são os campos que serão retornados após a conclusão da operação `addPerson`. Aqui está a parte final do exemplo:

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

Usando essa mutação, o resultado pode ser semelhante a:

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

Como você pode ver, a resposta retornou os valores que solicitamos no mesmo formato definido em nossa mutação. A prática recomendada é retornar todos os valores que foram modificados para reduzir a confusão e a necessidade de mais consultas no futuro. As mutações permitem que você inclua várias operações em seu escopo. Eles serão executados sequencialmente na ordem listada na mutação. Por exemplo, se criarmos outra operação chamada `addOccupation` que adiciona cargos à fonte de dados, podemos chamar isso na mutação depois de `addPerson`. `addPerson` será tratado primeiro, seguido por `addOccupation`.

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

As assinaturas são usadas [WebSockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications)para abrir uma conexão bidirecional duradoura entre o servidor e seus clientes. Normalmente, um cliente assina ou escuta o servidor. Sempre que o servidor fizer uma alteração no servidor ou realizar um evento, o cliente assinante receberá as atualizações. Esse tipo de protocolo é útil quando vários clientes são assinantes e precisam ser notificados sobre alterações que estão acontecendo no servidor ou em outros clientes. Por exemplo, as assinaturas podem ser usadas para atualizar feeds de mídia social. Pode haver dois usuários, o usuário A e o usuário B, que são assinantes das atualizações automáticas de notificação sempre que recebem mensagens diretas. O usuário A no cliente A poderia enviar uma mensagem direta para o usuário B no cliente B. O cliente do usuário A enviaria a mensagem direta, que seria processada pelo servidor. O servidor então enviaria a mensagem direta para a conta do Usuário B enquanto enviaria uma notificação automática para o Cliente B.

Aqui está um exemplo de uma `Subscription` que poderíamos adicionar ao exemplo do esquema:

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

O campo `personAdded` enviará uma mensagem aos clientes inscritos sempre que uma nova `Person` for adicionada à fonte de dados. Supondo que tenhamos uma implementação de resolvedor para `personAdded`, agora podemos usar a assinatura. Embora o tipo `Subscription` exista, precisamos chamá-lo explicitamente para que ele seja executado no código do aplicativo. Isso pode ser feito usando a palavra-chave `subscription`:

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

A assinatura é chamada de `personAddedOperation` e a operação é `personAdded`. `personAdded`retornará os campos `id` e `name` de novas instâncias de `Person`. Analisando o exemplo de mutação, adicionamos uma `Person` usando esta operação:

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

Se nossos clientes tiverem assinatura para receber atualizações da `Person` recém-adicionada, eles poderão ver isso após as execuções de `addPerson`:

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

Veja abaixo um resumo do que as assinaturas oferecem:

As assinaturas são canais bidirecionais que permitem que o cliente e o servidor recebam atualizações rápidas, mas constantes. Eles normalmente usam o WebSocket protocolo, que cria conexões padronizadas e seguras.

As assinaturas são ágeis, pois reduzem a sobrecarga de configuração da conexão. Depois de fazer uma assinatura, o cliente pode continuar executando essa assinatura por longos períodos. Eles geralmente usam recursos de computação de forma eficiente, permitindo que os desenvolvedores personalizem a vida útil da assinatura e configurem quais informações serão solicitadas.

No geral, o cliente pode fazer várias assinaturas ao mesmo tempo. No que diz respeito AWS AppSync, as assinaturas são usadas apenas para receber atualizações em tempo real do serviço. AWS AppSync Elas não podem ser usadas para realizar consultas ou mutações.

A principal alternativa às assinaturas é a sondagem, que envia consultas em intervalos definidos para solicitar dados. Esse processo geralmente é menos eficiente do que as assinaturas e sobrecarrega muito o cliente e o back-end.

------

Algo que não foi mencionado em nosso exemplo de esquema é o fato de que seus tipos de objetos especiais também devem ser definidos em um `schema` raiz. Então, quando você exporta um esquema AWS AppSync, ele pode ter a seguinte aparência:

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

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

.
.
.

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

------

## Enumerações
<a name="enum-components"></a>

Enumerações, ou enums, são escalares especiais que limitam os argumentos legais que um tipo ou campo pode ter. Isso significa que sempre que uma enum for definida no esquema, seu tipo ou campo associado será limitado aos valores da enum. As enums são serializadas como escalares de string. Observe que diferentes linguagens de programação podem lidar com enumerações do GraphQL de forma diferente. Por exemplo, não JavaScript tem suporte nativo para enum, portanto, os valores enum podem ser mapeados para valores int em vez disso.

As enums são definidas usando a palavra-chave `enum`. Veja um exemplo abaixo:

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

Ao chamar o `trafficLights` enum, os argumentos só podem ser `solidRed`, `solidYellow`, `solidGreen`, etc. É comum usar enums para descrever coisas que têm um número distinto, mas limitado, de opções.

## Uniões/Interfaces
<a name="union-interface-components"></a>

Consulte [Interfaces e uniões](https://docs.aws.amazon.com/appsync/latest/devguide/interfaces-and-unions.html) no GraphQL.

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

Os campos existem dentro do escopo de um tipo e contêm o valor solicitado do serviço do GraphQL. Eles são muito semelhantes às variáveis de outras linguagens de programação. Por exemplo, veja um tipo de objeto `Person`:

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

Nesse caso, os campos são `name` e `age` e contêm um valor `String` e `Int` respectivamente. Campos de objetos como os mostrados acima podem ser usados como entradas nos campos (operações) de suas consultas e mutações. Por exemplo, veja a `Query` a seguir:

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

O campo `people` está solicitando todas as instâncias de `Person` da fonte de dados. Ao adicionar ou recuperar uma `Person` em seu servidor GraphQL, você pode esperar que os dados sigam o formato dos seus tipos e campos, ou seja, a estrutura dos seus dados no esquema determina como eles serão estruturados na sua resposta:

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

Os campos desempenham um papel importante na estruturação dos dados. Há algumas propriedades adicionais explicadas abaixo que podem ser aplicadas aos campos para maior personalização.

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

As listas retornam todos os itens de um tipo especificado. Uma lista pode ser adicionada ao tipo de um campo usando colchetes `[]`: 

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

Na `Query`, os colchetes ao redor de `Person` indicam que você deseja retornar todas as instâncias de `Person` da fonte de dados como uma matriz. Na resposta, os valores `name` e `age` de cada `Person` serão retornados como uma única lista 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
      .
      .
    ]
  }
}
```

Não é necessário usar apenas tipos de objetos especiais. Você também pode usar listas nos campos de tipos de objetos regulares.

## Não nulos
<a name="non-null-components"></a>

Os valores não nulos indicam um campo que não pode ser nulo na resposta. Você pode definir um campo como não nulo usando o símbolo `!`:

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

O campo `name` não pode ser explicitamente nulo. Se você consultasse a fonte de dados e fornecesse uma entrada nula para esse campo, um erro seria gerado.

Você pode combinar listas e não 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
}
```

No caso de uso 1, a lista não pode conter itens nulos. No caso de uso 2, a lista em si não pode ser definida como nula. No caso de uso 3, a lista e seus itens não podem ser nulos. No entanto, em qualquer um dos casos, você ainda pode retornar listas vazias.

Como você pode ver, há muitos componentes móveis no GraphQL. Nesta seção, mostramos a estrutura de um esquema simples e os diferentes tipos e campos que um esquema permite. Na seção a seguir, você descobrirá os outros componentes de uma API GraphQL e como eles funcionam com o esquema.