

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

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

Uma API GraphQL padrão é composta por um único esquema que manipula a forma dos dados que serão consultados. Seu esquema está vinculado a uma ou mais de suas fontes de dados, como um banco de dados ou uma função do Lambda. Entre os dois, há um ou mais resolvedores que lidam com a lógica de negócios de suas solicitações. Cada componente desempenha um papel importante na implementação do GraphQL. As seções a seguir apresentarão esses três componentes e o papel que eles desempenham no serviço GraphQL.

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


**Topics**
+ [Esquemas do GraphQL](schema-components.md)
+ [Fontes de dados](data-source-components.md)
+ [Resolvedores](resolver-components.md)

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

# Fontes de dados
<a name="data-source-components"></a>

Na seção anterior, aprendemos que um esquema define a forma dos seus dados. No entanto, não explicamos de onde vieram esses dados. Em projetos reais, seu esquema é como um gateway que manipula todas as solicitações feitas ao servidor. Quando uma solicitação é feita, o esquema atua como o único endpoint que interage com o cliente. O esquema acessa, processa e retransmite dados da fonte de dados para o cliente. Veja o infográfico abaixo:

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


AWS AppSync e o GraphQL implementam de forma excelente as soluções Backend For Frontend (BFF). Eles trabalham em conjunto para reduzir a complexidade em grande escala ao abstrair o back-end. Caso seu serviço use fontes de dados e/ou microsserviços diferentes, você pode basicamente abstrair parte da complexidade definindo a forma dos dados de cada fonte (subgráfico) em um único esquema (supergráfico). Isso significa que sua API GraphQL não precisa usar uma única fonte de dados. Você pode associar qualquer número de fontes de dados à sua API GraphQL e especificar no seu código como elas interagirão com o serviço.

Como você pode ver no infográfico, o esquema do GraphQL contém todas as informações que os clientes precisam para solicitar dados. Isso significa que tudo pode ser processado em uma única solicitação, em vez de várias solicitações, como é o caso do REST. Essas solicitações passam pelo esquema, que é o único endpoint do serviço. Quando as solicitações são processadas, um resolvedor (explicado na próxima seção) executa seu código para processar os dados da fonte de dados relevante. Quando a resposta for retornada, o subgráfico vinculado à fonte de dados será preenchido com os dados no esquema. 

AWS AppSync oferece suporte a vários tipos diferentes de fontes de dados. Na tabela abaixo, descreveremos cada tipo, listaremos alguns dos benefícios de cada um e forneceremos links úteis que trazem mais contexto.


| Fonte de dados | Description | Benefícios | Informações complementares | 
| --- | --- | --- | --- | 
| Amazon DynamoDB | “O Amazon DynamoDB é um serviço de banco de dados NoSQL totalmente gerenciado que fornece uma performance rápida e previsível com escalabilidade integrada. O DynamoDB permite que você transfira os encargos administrativos de operação e escalabilidade de um banco de dados distribuído. Assim, você não precisa se preocupar com provisionamento, instalação e configuração de hardware, replicação, correção de software nem escalabilidade de cluster. Além disso, o DynamoDB oferece criptografia em repouso, o que elimina a carga e a complexidade operacionais envolvidas na proteção de dados confidenciais.” |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/pt_br/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/pt_br/appsync/latest/devguide/data-source-components.html)  | 
| AWS Lambda | “AWS Lambda é um serviço de computação que permite executar código sem provisionar ou gerenciar servidores.O Lambda executa seu código em uma infraestrutura de computação de alta disponibilidade e executa toda a administração dos recursos computacionais, incluindo manutenção do servidor e do sistema operacional, provisionamento e escalabilidade automática da capacidade e registro em log do código. Com o Lambda, tudo o que você precisa fazer é fornecer seu código em um dos runtimes de linguagens compatíveis com o Lambda.” |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/pt_br/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/pt_br/appsync/latest/devguide/data-source-components.html)  | 
| OpenSearch | “O Amazon OpenSearch Service é um serviço gerenciado que facilita a implantação, a operação e a escalabilidade de OpenSearch clusters na AWS nuvem. O Amazon OpenSearch Service suporta OpenSearch e lega o Elasticsearch OSS (até a versão 7.10, a versão final de código aberto do software). Ao criar um cluster, você tem a opção de escolher qual mecanismo de pesquisa deseja usar.**OpenSearch**é um mecanismo de pesquisa e análise totalmente de código aberto para casos de uso como análise de registros, monitoramento de aplicativos em tempo real e análise de fluxo de cliques. Para obter mais informações, consulte a [documentação do OpenSearch](https://opensearch.org/docs/).**O Amazon OpenSearch Service** provisiona todos os recursos do seu OpenSearch cluster e o executa. Ele também detecta e substitui automaticamente os nós de OpenSearch serviço com falha, reduzindo a sobrecarga associada às infraestruturas autogerenciadas. Você pode escalar seu cluster com uma única chamada de API ou alguns cliques no console.” |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/pt_br/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/pt_br/appsync/latest/devguide/data-source-components.html)  | 
| Endpoints de HTTP | Você pode usar endpoints HTTP como fontes de dados. AWS AppSync pode enviar solicitações aos endpoints com as informações relevantes, como parâmetros e carga útil. A resposta HTTP será exposta ao resolvedor, que retornará a resposta final após concluir suas operações. |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/pt_br/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/pt_br/appsync/latest/devguide/data-source-components.html)  | 
| Amazon EventBridge | “EventBridge é um serviço sem servidor que usa eventos para conectar componentes do aplicativo, facilitando a criação de aplicativos escaláveis orientados por eventos. Use-o para rotear eventos de fontes como aplicativos, AWS serviços e software de terceiros desenvolvidos internamente para aplicativos de consumo em toda a sua organização. EventBridge fornece uma maneira simples e consistente de ingerir, filtrar, transformar e entregar eventos para que você possa criar novos aplicativos rapidamente.” |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/pt_br/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/pt_br/appsync/latest/devguide/data-source-components.html)  | 
| Bancos de dados relacionais | “O Amazon Relational Database Service (Amazon RDS) é um serviço web que facilita a configuração, a operação e a escalabilidade de um banco de dados relacional na nuvem. AWS Ele fornece capacidade econômica e redimensionável para um banco de dados relacional padrão do setor e gerencia tarefas comuns de administração de banco de dados. |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/pt_br/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/pt_br/appsync/latest/devguide/data-source-components.html)  | 
| Fonte de dados none | Se você não pretende usar uma fonte de dados, pode defini-la como none. Uma fonte de dados none, embora ainda seja explicitamente categorizada como fonte de dados, não é um meio de armazenamento. Apesar disso, ela ainda é útil em algumas instâncias para manipulação e passagem de dados. |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/pt_br/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/pt_br/appsync/latest/devguide/data-source-components.html)  | 

**dica**  
Para obter mais informações sobre como as fontes de dados interagem AWS AppSync, consulte [Anexando uma fonte de dados](https://docs.aws.amazon.com//appsync/latest/devguide/attaching-a-data-source.html).

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

Nas seções anteriores, você aprendeu sobre os componentes do esquema e da fonte de dados. Agora, precisamos abordar como o esquema e as fontes de dados interagem. Tudo começa com o resolvedor.

Um resolvedor é uma unidade de código que controla como os dados desse campo serão resolvidos quando uma solicitação for feita ao serviço. Os resolvedores são anexados a campos específicos dentro dos seus tipos em seu esquema. Eles costumam ser usados para implementar as operações de mudança de estado para suas operações de campo de consulta, mutação e assinatura. O resolvedor processará a solicitação de um cliente e retornará o resultado, que pode ser um grupo de tipos de saída, como objetos ou escalares:

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


## Runtime do resolvedor
<a name="resolver-components-runtime"></a>

Em AWS AppSync, você deve primeiro especificar um tempo de execução para seu resolvedor. Um runtime do resolvedor indica o ambiente no qual um resolvedor é executado. Também determina o idioma em que seus resolvedores serão escritos. AWS AppSync atualmente suporta APPSYNC\$1JS JavaScript e Velocity Template Language (VTL). Consulte os [recursos JavaScript de tempo de execução para resolvedores e funções](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-util-reference-js.html) JavaScript ou a [referência do utilitário de modelo de mapeamento Resolver](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-util-reference.html) para VTL.

## Estrutura do resolvedor
<a name="resolver-components-structure"></a>

Em termos de código, os resolvedores podem ser estruturados de duas maneiras. Existem resolvedores de **unidades** e de **pipeline**.

### Resolvedores de unidade
<a name="resolver-components-unit"></a>

Um resolvedor de unidade é composto de código que define um único manipulador de solicitação e resposta que é executado em uma fonte de dados. O manipulador da solicitação usa um objeto de contexto como argumento e retorna a payload da solicitação usada para chamar sua fonte de dados. O manipulador de respostas recebe uma payload da fonte de dados com o resultado da solicitação executada. O manipulador de respostas transforma a payload em uma resposta do GraphQL para resolver o campo do GraphQL.

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


### Resolvedores de pipeline
<a name="resolver-components-pipeline"></a>

Ao implementar resolvedores, eles seguem uma estrutura geral:
+ **Etapa Anterior**: quando uma solicitação é feita pelo cliente, os resolvedores dos campos do esquema que estão sendo usados (normalmente consultas, mutações e assinaturas) recebem os dados da solicitação. O resolvedor começará a processar os dados da solicitação com um manipulador de etapas anteriores, o que permite que algumas operações de pré-processamento sejam executadas antes que os dados passem pelo resolvedor.
+ **Função(ões)**: Após a execução da etapa anterior, a solicitação é passada para a lista de funções. A primeira função na lista será executada na fonte de dados. Uma função é um subconjunto do código do resolvedor contendo seu próprio manipulador de solicitações e respostas. Um manipulador de solicitações pegará os dados da solicitação e executará operações na fonte de dados. O manipulador de respostas processará a resposta da fonte de dados antes de passá-la de volta para a lista. Se houver mais de uma função, os dados da solicitação serão enviados para a próxima função a ser executada na lista. As funções na lista serão executadas na ordem definida pelo desenvolvedor. Depois que todas as funções forem executadas, o resultado final será passado para a etapa posterior.
+ **Etapa Posterior**: a etapa Posterior é uma função do manipulador que permite realizar algumas operações finais na resposta da função final antes de passá-la para a resposta do GraphQL.

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


## Estrutura do manipulador do resolvedor
<a name="resolver-components-handlers"></a>

Os manipuladores são normalmente funções chamadas `Request` e `Response`:

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

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

Em um resolvedor de unidades, haverá apenas um conjunto dessas funções. Em um resolvedor de pipeline, haverá um conjunto dessas etapas para a etapa Anterior e etapa Posterior e um conjunto adicional por função. Para visualizar esse caso, vamos analisar um tipo `Query` simples:

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

Essa é uma consulta simples com um campo chamado `helloWorld` do tipo `String`. Vamos supor que sempre queremos que esse campo retorne a string “Hello World”. Para implementar esse comportamento, precisamos adicionar o resolvedor a esse campo. Em um resolvedor de unidades, poderíamos adicionar algo assim:

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

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

A `request` pode ser deixada em branco porque não estamos solicitando ou processando dados. Também podemos supor que nossa fonte de dados seja `None`, indicando que esse código não precisa realizar nenhuma invocação. A resposta simplesmente retorna “Hello World”. Para testar esse resolvedor, precisamos fazer uma solicitação usando o tipo de consulta:

```
query helloWorldTest {
  helloWorld
}
```

Essa é uma consulta chamada `helloWorldTest` que retorna o campo `helloWorld`. Quando executado, o resolvedor do campo `helloWorld` também executa e retorna a resposta:

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

Retornar constantes como essa é a coisa mais simples que você pode fazer. Na realidade, você retornará entradas, listas e muito mais. O exemplo a seguir é mais complicado:

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

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

Aqui estamos retornando uma lista de `Books`. Vamos supor que estejamos usando uma tabela do DynamoDB para armazenar dados do livro. Os manipuladores podem ser semelhantes a estes:

```
/**
 * 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;
}
```

Nossa solicitação usou uma operação de verificação integrada para pesquisar todas as entradas na tabela, armazenar as descobertas no contexto e depois passá-las para a resposta. A resposta pegou os itens do resultado e os retornou na resposta:

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

        ...

      ]
    }
  }
}
```

## Contexto do resolvedor
<a name="resolver-components-context"></a>

Em um resolvedor, cada etapa na cadeia de manipuladores deve estar ciente do estado dos dados das etapas anteriores. O resultado de um manipulador pode ser armazenado e passado para outro como argumento. O GraphQL define quatro argumentos básicos do resolvedor:


****  

| Argumentos básicos do resolvedor | Description | 
| --- | --- | 
| obj, root, parent etc. | O resultado do pai. | 
| args | Os argumentos fornecidos ao campo na consulta do GraphQL. | 
| context | Um valor que é fornecido a cada resolvedor e contém informações contextuais importantes, como o usuário atualmente conectado ou o acesso a um banco de dados. | 
| info | Um valor que contém informações específicas do campo relevantes para a consulta atual, bem como os detalhes do esquema. | 

Em AWS AppSync, o argumento `[context](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference-js.html)` (ctx) pode conter todos os dados mencionados acima. Trata-se um objeto criado por solicitação e contém dados como credenciais de autorização, dados de resultados, erros, metadados de solicitações etc. O contexto é uma maneira fácil para os programadores manipularem dados provenientes de outras partes da solicitação. Considere este trecho novamente:

```
/**
 * 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;
}
```

A solicitação recebe o contexto (ctx) como argumento; esse é o estado da solicitação. Ele executa uma varredura de todos os itens em uma tabela e, em seguida, armazena o resultado no contexto em `result`. O contexto é então passado para o argumento de resposta, que acessa o `result` e retorna seu conteúdo.

## Solicitações e análises
<a name="resolver-ast"></a>

Quando você faz uma consulta ao seu serviço do GraphQL, ela deve passar por um processo de análise e validação antes de ser executada. Sua solicitação será analisada e traduzida em uma árvore de sintaxe abstrata. O conteúdo da árvore é validado por meio da execução de vários algoritmos de validação em relação ao seu esquema. Após a etapa de validação, os nós da árvore são percorridos e processados. Os resolvedores são invocados, os resultados são armazenados no contexto e a resposta é retornada. Por exemplo, considere esta consulta:

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

Estamos retornando `Person` com campos `name` e `age`. Ao executar essa consulta, a árvore será semelhante a esta:

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


Na árvore, parece que essa solicitação pesquisará a raiz de `Query` no esquema. Dentro da consulta, o campo `Person` será resolvido. Com base em exemplos anteriores, sabemos que isso pode ser uma entrada do usuário, uma lista de valores, etc. A `Person` provavelmente está vinculada a um tipo de objeto que contém os campos de que precisamos (`name` e `age`). Depois que esses dois campos secundários são encontrados, eles são resolvidos na ordem indicada (`name` seguido por `age`). Depois que a árvore for completamente resolvida, a solicitação será concluída e enviada de volta ao cliente.