Neste tutorial, mostraremos como usar os Módulos GraphQL para dividir seu aplicativo GraphQL em módulos simples, reutilizáveis ​​e baseados em recursos.

Abordaremos o seguinte:

O que são módulos GraphQL?

Módulos GraphQL é um conjunto de bibliotecas e diretrizes projetadas para ajudá-lo a criar módulos reutilizáveis, sustentáveis, testáveis ​​e extensíveis. O conceito básico por trás dos Módulos GraphQL é separar seu servidor GraphQL em módulos menores, independentes e baseados em recursos.

Módulos GraphQL ajudam a implementar a separação de interesses padrão de design no GraphQL, permitindo que você escreva módulos simples que fazem apenas o que precisam. Isso torna seu código mais fácil de escrever, testar e manter.

A estrutura de uma implementação inicial básica de um servidor GraphQL seria mais ou menos assim:

GraphQL Server Basic Exemplo de implementação
Implementação básica do GraphQL

Conforme seu aplicativo GraphQL cresce, também aumenta a complexidade do seu esquema e resolvedores, o que pode dificultar a manutenção do esquema. Para organizar seu código, você desejará separar seus tipos de esquema e seus resolvedores associados em vários arquivos ou módulos e adicionar qualquer coisa relacionada a uma parte específica do aplicativo em um módulo. A biblioteca de módulos graphql ajuda a facilitar esse processo gradual.

Com graphql-modules , seu aplicativo seria estruturado da seguinte maneira:

GraphQL-Estrutura do aplicativo da biblioteca de módulos

Por que usar os módulos GraphQL?

A maioria dos desenvolvedores que usam os Módulos GraphQL selecionam o conjunto de ferramentas para seus módulos reutilizáveis, estrutura escalável, caminho claro para o crescimento e capacidade de teste. Vamos dar uma olhada em alguns dos motivos pelos quais os desenvolvedores recorrem aos Módulos GraphQL para escrever módulos reutilizáveis ​​e extensíveis para seus aplicativos GraphQL.

  • Módulos reutilizáveis ​​: os módulos são definidos por seu esquema GraphQL em partes menores, que você pode mover e reutilizar posteriormente
  • Estrutura escalonável : cada módulo tem um limite claramente definido, o que torna o gerenciamento de várias equipes e recursos, vários microsserviços e servidores muito mais fácil. Os módulos podem se comunicar entre si usando mensagens personalizadas
  • Caminho claro para o crescimento : separando os recursos em módulos, é fácil ver como seu aplicativo pode crescer de módulos muito simples e rápidos de um único arquivo para uma equipe escalável de vários arquivos , módulos-repo e-server
  • Testabilidade : testar pequenos trechos de código é mais fácil do que testar trechos maiores de código. Os Módulos GraphQL fornecem um conjunto de ferramentas rico para testar e simular seu aplicativo

Como funcionam os módulos GraphQL

Usaremos um aplicativo de biblioteca simples para mostrar como dividir seu aplicativo GraphQL em módulos. O aplicativo terá os módulos Book, Author e Genre e cada módulo será composto de definições de tipo e funções de resolução.

Criando um módulo GraphQL

Para começar a usar graphql-modules , tudo que você precisa é instalar seu pacote e o graphql :

//NPM
npm install-salvar graphql graphql-modules //Fio yarn adicionar módulos graphql graphql

Para criar um módulo, use createModule :

 import {createModule} de'graphql-modules'; export const myModule=createModule ({ id:'meu-módulo', dirname: __dirname, typeDefs: [ `type Query { olá: String! } `, ], resolvedores: { Inquerir: { Olá: ()=& gt;'mundo', }, },
});

Adicionar um id exclusivo ao módulo é importante porque o ajudará a localizar problemas na definição do tipo. O dirname , embora opcional, torna mais simples corresponder ao arquivo correto quando ocorre uma exceção.

Definições de tipo e resolvedores

Para definir esquemas, os Módulos GraphQL usam Schema Definition Language (SDL), assim como o GraphQL Schema.

Módulos GraphQL também implementam resolvedores da mesma maneira que qualquer outra implementação GraphQL. De acordo com a documentação dos Módulos GraphQL , os módulos criados pelos Módulos GraphQL podem detectar resolvedores duplicados, incorretos ou antigos (por exemplo , resolvedores que não correspondem às definições de tipo ou extensões).

Criação do módulo do livro

Veja como criar um módulo de livro para nosso aplicativo GraphQL:

//book.type.graphql
importar {gql} de'módulos do graphql'; export const Book=gql` tipo Query { livro (id: ID!): Livro } type Book { id: String título: String autor: autor resumo: String isbn: String gênero: [gênero] url: String }
`; //book.resolver.graphql export const BookResolver={ Inquerir: { livro (raiz, {id}) { Retorna { _eu fiz, título:"Para o farol", autor:"Virginia Woolf", resumo:"Resumo do livro", isbn:"12345678EDB" gênero: ["ficton"], url:"http://lighouse.com" }; }, }, Livro: { id (livro) { return book._id; }, título (livro) { return book.title; }, autor (livro) { return book.author; }, resumo (livro) { return book.summary; }, isbn (livro) { return book.isbn; }, gênero (livro) { return book.genre; }, url (livro) { return book.url; }, }, }, //book.module.graphql.ts
import {Book} from'./book.type.graphql';
import {BookResolver}'.book.resolver.graphql';
import {createModule} de'graphql-modules'; export const BookModule=createModule ({ id:'módulo de livro', dirname: __dirname, typeDefs: [Livro], resolvers: [BookResolvers]
});

Outros módulos

Para criar os módulos de autor e gênero para nosso aplicativo de biblioteca GraphQL de exemplo, siga as etapas abaixo:

//author.module.graphql.ts
import {Author} from'./author.type.graphql';
import {AuthorResolver}'.author.resolver.graphql';
import {createModule} de'graphql-modules'; export const AuthorModule=createModule ({ id:'módulo de livro', dirname: __dirname, typeDefs: [Autor], resolvers: [AuthorResolvers]
}); //genre.module.graphql.ts
import {Genre} de'./genre.type.graphql';
import {GenreResolver}'.genre.resolver.graphql';
import {createModule} de'graphql-modules'; export const GenreModule=createModule ({ id:'módulo de livro', dirname: __dirname, typeDefs: [gênero], resolvers: [GenreResolvers]
});

Mesclando esquemas com módulos GraphQL

Cada um dos módulos que definimos contribui com uma pequena parte de todo o esquema.

Para mesclar os esquemas, crie um aplicativo usando createApplication dos módulos GraphQL:

 import {createApplication} de'graphql-modules';
import {GenreModule} de'./genre/genre.module.graphql';
import {BookModule} de'./book/book.module.graphql';
import {AuthorModule} de'./author/author.module.graphql'; export const application=createApplication ({ módulos: [BookModule, AuthorModule, GenreModule],
});

O aplicativo contém seu esquema GraphQL e a implementação dele. Precisamos torná-lo consumível por um servidor GraphQL.

Módulos GraphQL têm várias implementações para servidores GraphQL populares, como Apollo , Express GraphQL e GraphQL Helix . Se você estiver usando o Apollo Servidor , você pode usar createSchemaForApollo para obter um esquema que seja adaptado para este servidor e se integre perfeitamente a ele:

 importar {ApolloServer} de'apollo-server';
import {application} from'./application'; esquema const=application.createSchemaForApollo (); const server=new ApolloServer ({ esquema,
}); server.listen (). then (({url})=& gt; { console.log (`& # x1f680; Servidor pronto em $ {url}`);
});

GraphQL context

No GraphQL, o contexto argumento é compartilhado por todos os resolvedores que estão executando para um operação particular. Você pode usar context para compartilhar o estado por operação, incluindo dados de autenticação, instâncias do dataloader, etc.

Em Módulos GraphQL, context está disponível como o terceiro argumento para cada resolvedor e é compartilhado entre os módulos:

 const resolvers={ Inquerir: { myQuery (root, args, context, info) { //... }, },
};

Injeção de dependência

Conforme seu aplicativo se expande, você pode precisar separar certas lógicas de negócios do resolvedor em um serviço. Os módulos GraphQL são compatíveis com injeção de dependência (DI), o que ajuda a disponibilizar seus serviços para os resolvedores.

Dependências são serviços ou objetos que um resolvedor precisa para realizar sua função. Portanto, em vez de criar um serviço, um resolvedor o solicita de um recurso externo.

É importante observar que a injeção de dependência só faz sentido quando sua base de código é muito grande e você precisa agir rápido.

Para usar o DI dos Módulos GraphQL, existem alguns termos que você deve entender:

  • injetor é um objeto no sistema DI que pode encontrar uma dependência nomeada em seu cache ou criar uma dependência usando um provedor configurado e, em seguida, torná-la disponível para um módulo ou para todo o aplicativo, dependendo de seu escopo
  • InjectionToken é um símbolo (token) ou classe (classe de serviço) que representa um objeto ou qualquer valor no espaço de injeção de dependência
  • provedor é uma maneira de definir um valor e combiná-lo com uma classe Token ou Serviço . Ele fornece o valor de um token de injeção específico. Os provedores são injetados em resolvedores ou outros serviços.

Existem três tipos de provedores: provedores de classe, provedores de valor e provedores de fábrica. Vamos dar uma olhada em cada um.

Provedor de aulas

Um provedor de classe cria uma instância de uma classe e a disponibiliza para o injetor. Para que uma classe de serviço seja usada como um provedor, ela deve ser decorada com o decorador @Injectable () :

//book.service.ts
import {Injectable} from'graphql-modules'; @Injectable ()
exportar classe BookService {} //book.module.graphql.ts
import {Book} from'./book.type.graphql';
import {BookResolver}'.book.resolver.graphql';
import {createModule} de'graphql-modules'; export const BookModule=createModule ({ id:'módulo de livro', dirname: __dirname, typeDefs: [Livro], resolvers: [BookResolvers], provedores: [BookService]
});

O objeto context está disponível em todos os resolvedores. Este objeto context contém outro objeto, injector , que pode ser usado para acessar uma classe de serviço fornecida em um resolvedor.

//book.resolver.graphql
importar {BookService} de'./book.service.ts'
export const BookResolver={ Inquerir: { livro (raiz, {id}, contexto) { const bookService=context.injector.get (BookService); Retorna { _eu fiz, //... }; }, }, Livro: { id (livro) { return book._id; }, //... }, },

Para usá-lo em uma classe, basta solicitá-lo no construtor.

 import {Injectable} from'graphql-modules';
importar {BookService} de'./book'; @Injectable ()
class AuthorService { construtor (bookService privado: BookService) {} allbooks (authorId) { retornar this.bookService.books (authorId) }
}

Provedor de valor

O provedor de valor fornece um valor pronto para uso. Requer um token que represente um valor, InjectionToken ou uma classe.

 import {InjectionToken} de'graphql-modules';
const ApiKey=new InjectionToken & lt; string & gt; ('api-key');
import {createModule} de'graphql-modules'; export const BookModule=createModule ({ id:'módulo de livro', /*... */ provedores: [ { fornecer: ApiKey, useValue:'my-api-key', }, ],
});

Fornecedor de fábrica

O provedor de fábrica é uma função que fornece um valor. É útil quando você precisa criar um valor dependente dinamicamente com base em informações que não estariam disponíveis até o tempo de execução. Você pode tomar uma decisão informada sobre qual valor retornar com base em certas condições do estado do aplicativo.

O provedor de fábrica também é útil para criar uma instância de uma classe, como ao usar bibliotecas de terceiros. A função de fábrica pode ter um argumento opcional com dependências, se houver.

 import {InjectionToken} de'graphql-modules';
const ApiKey=new InjectionToken & lt; string & gt; ('api-key');
import {createModule} de'graphql-modules'; export const BookModule=createModule ({ id:'módulo de livro', /*... */ provedores: [ { fornecer: ApiKey, useFactory (config: Config) { if (context.environment) { return'my-api-key'; } return null; }, deps: [Config], }, ],
});

Acessar um token em um resolvedor é semelhante a acessar um serviço. O decorador @Inject é usado para acessar um token em um construtor.

 import {Injectable} from'graphql-modules';
importar {BookService} de'./book'; @Injectable ()
class AuthorService { construtor (@Inject (ApiKey) chave privada: string, bookService privado: BookService) {} allbooks (authorId) { retornar this.bookService.books (authorId) }
}

Escopos GraphQL

Cada token ou provedor possui um escopo que é usado para definir seu ciclo de vida. Scope pode ser um singleton ou uma operação. Cada provedor ou token definido como um singleton é criado uma vez e a mesma instância está disponível para todas as operações GraphQL de entrada. Um provedor de singleton vive durante todo o ciclo de vida do aplicativo e é o escopo padrão e recomendado para módulos GraphQL.

Os provedores de operação, por outro lado, são criados por contexto de execução ou para cada operação GraphQL que o requer. Um provedor de operação vive apenas o ciclo de vida da operação GraphQL que o instanciou.

//genre.service.ts
import {Injectable, Scope} from'graphql-modules'; @Injectable ({ escopo: Scope.Operation,
})
exportar classe GenreService {} //genre.module.graphql.ts
import {Genre} de'./genre.type.graphql';
import {GenreResolver}'.genre.resolver.graphql';
import {createModule} de'graphql-modules';
importar {GenreService} de'./genre.service.ts' export const GenreModule=createModule ({ id:'módulo de livro', dirname: __dirname, typeDefs: [gênero], resolvers: [GenreResolvers], provedores: [GenreService]
});

Quando GenreService não é chamado por resolvedores, o serviço não é criado.

Conclusão

O código que pode ser mantido é facilmente escalonável e os Módulos GraphQL visam ajudá-lo a conseguir exatamente isso.

Não vale a pena que a separação ou modularização de código só aconteça durante o desenvolvimento. No entanto, em tempo de execução, um esquema unificado ainda é servido.

Agora você deve ter tudo de que precisa para começar a usar os módulos GraphQL. Para aprender sobre outros conceitos avançados, como assinatura, middleware, contexto de execução e ganchos de ciclo de vida, você pode verificar GraphQL Module s documentação .

O post tutorial dos Módulos GraphQL: Como modularizar O esquema GraphQL apareceu primeiro no LogRocket Blog .

Source link