Resolvedores - AWS AppSync

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

Resolvedores

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:

Runtime do resolvedor

No AWS AppSync, você deve primeiro especificar um runtime para seu resolvedor. Um runtime do resolvedor indica o ambiente no qual um resolvedor é executado. Isso também determina a linguagem em que seus resolvedores serão escritos. O AWS AppSync atualmente é compatível com o APPSYNC_JS para JavaScript e Velocity Template Language (VTL). Consulte os Atributos e funções de JavaScript runtime para resolvedores para JavaScript ou a Referência do utilitário de modelo de mapeamento de resolvedor para VTL.

Estrutura do resolvedor

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

Resolvedores de unidade

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.

Resolvedores de pipeline

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.

Estrutura do manipulador do resolvedor

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

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 Descrição
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.

No AWS AppSync, o argumento context (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

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:

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.