Servidor simples com GraphQL em vez de REST, implementação em java


Me ofereceram para me familiarizar com o GraphQL. Veja se você pode se inscrever no trabalho. Após a pesquisa, percebi que a maioria das informações está em inglês e é parcialmente antiga, existem 3 versões da biblioteca e já existem 5. Eu quero preencher essa lacuna. Nesta opção, haverá um exemplo em servlets, ou seja, sem mola e sem bota.

A parte teórica:
GraphQL - uma nova visão da API. Parte 1 de VladimirZaets
Comparação bevalorous de REST e GraphQL

Mostrarei o código imediatamente, porque o GraphQL é uma abstração. E se você discutir a abstração por um longo tempo, poderá se perder. O original está aqui .

Eu mudei um pouco o código, porque versões mais recentes não possuem algumas classes.

Crie um projeto vazio do maven. Adicione dependências ao piquenique:

<dependency> <groupId>com.graphql-java</groupId> <artifactId>graphql-java</artifactId> <version>8.0</version> </dependency> <dependency> <groupId>com.graphql-java</groupId> <artifactId>graphql-java-tools</artifactId> <version>5.0.0</version> </dependency> <dependency> <groupId>com.graphql-java</groupId> <artifactId>graphql-java-servlet</artifactId> <version>5.0.0</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1</version> <scope>provided</scope> </dependency> 

Para não pensar no servidor, pegue o jetty:

  <plugin> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>9.4.6.v20170531</version> </plugin> 

Diferenças do tutorial:

  1. A herança do SimpleGraphQLServlet com uma chamada para o construtor agora está "obsoleta"; você deve usar o construtor, o que é impossível na herança, usar a composição.
  2. No servlet, você pode criar um objeto SimpleGraphQLServlet.
  3. GraphQLRootResolver - não é mais, você pode usar os específicos: GraphQLMutationResolver e GraphQLQueryResolver

A fundação está pronta. Faremos de acordo com o tutorial, sem mola ou JAX-RS. Em geral, um servlet regular:

 @WebServlet(urlPatterns = "/graphql") public class GraphQLEndpoint extends HttpServlet { private SimpleGraphQLServlet graph; public GraphQLEndpoint() { graph = SimpleGraphQLServlet.builder(buildSchema()).build(); } private static GraphQLSchema buildSchema() { LinkRepository linkRepository = new LinkRepository(); return SchemaParser.newParser() .file("schema.graphqls") .resolvers(new Query(linkRepository), new Mutation(linkRepository)) .build() .makeExecutableSchema(); } @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { graph.service(req, resp); } } 

Nele, o método de serviço passa os dados para o SimpleGraphQLServlet. Tudo neste negócio termina.

Código regular (Link e LinkRepository)
 public class Link { private final String url; private final String description; public Link(String url, String description) { this.url = url; this.description = description; } public String getUrl() { return url; } public String getDescription() { return description; } } 

 public class LinkRepository { private final List<Link> links; public LinkRepository() { links = new ArrayList<>(); //add some links to start off with links.add(new Link("http://howtographql.com", "Your favorite GraphQL page")); links.add(new Link("http://graphql.org/learn/", "The official docks")); } public List<Link> getAllLinks() { return links; } public void saveLink(Link link) { links.add(link); } } 


Agora, o código de solicitação (solicitações GET no REST) ​​e mutações (solicitações de alterações)
 import com.coxautodev.graphql.tools.GraphQLQueryResolver; public class Query implements GraphQLQueryResolver { private final LinkRepository linkRepository; public Query(LinkRepository linkRepository) { this.linkRepository = linkRepository; } public List<Link> allLinks() { return linkRepository.getAllLinks(); } } 

 import com.coxautodev.graphql.tools.GraphQLMutationResolver; public class Mutation implements GraphQLMutationResolver { private final LinkRepository linkRepository; public Mutation(LinkRepository linkRepository) { this.linkRepository = linkRepository; } public Link createLink(String url, String description) { Link newLink = new Link(url, description); linkRepository.saveLink(newLink); return newLink; } } 


Começamos com jetty: run e jogamos request:

 http://localhost:8080/graphql?query={allLinks{url}} 

Temos a resposta:

 {"data": {"allLinks": [ {"url":"http://howtographql.com"}, {"url":"http://graphql.org/learn/"} ] } } 

E aqui está o primeiro ponto principal: temos 2 campos, URL e descrição, e recebemos apenas URL em resposta. E com razão, porque nós apenas url e solicitamos {allLinks {url}} na solicitação. Se você alterar a solicitação para:

 http://localhost:8080/graphql?query={allLinks{url,description}} 

a resposta é:

 {"data": {"allLinks": [ { "url":"http://howtographql.com", "description":"Your favorite GraphQL page" }, { "url":"http://graphql.org/learn/", "description":"The official docks" } ] } } 

Agora temos URL e descrição. E tudo porque pedimos a eles.

Solicitação de alteração de dados.

É um pouco mais complicado e muito mais fácil de usar o utilitário de interface do usuário.

1. Vamos para o endereço
2. Copie o arquivo index.html inteiro
3. Substitua 2 linhas:

São eles:

 <link rel="stylesheet" href="./node_modules/graphiql/graphiql.css" /> <script src="./node_modules/graphiql/graphiql.js"></script> 


Sobre estes:

 <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/graphiql@0.11.2/graphiql.css" /> <script src="//cdn.jsdelivr.net/npm/graphiql@0.11.2/graphiql.js"></script> 


4. Substitua index.html no projeto ... \ src \ main \ webapp \ index.html pelo que acabou de ser baixado.

Reiniciamos o projeto, vamos para localhost: 8080 e chegamos a essa página


Primeiro bônus: à esquerda está o botão Documentos, que oculta a documentação pronta para o seu projeto. Não são muitos os campos que podem ser solicitados e os métodos que podem ser chamados e o que passar neles.



Também fornece seus conjuntos de carros favoritos.



Agora, enviaremos uma solicitação de mutação. Temos uma mutação, "adicione um link".



texto da consulta:
 mutation createLink{ createLink(url:"http://test.com", description:"test mutation"){ url description } } 

Um pouco mais tarde, descobri que a palavra mutação é suficiente (createLink depois que não é necessário escrever)

 mutation { createLink(url:"http://test.com", description:"test mutation"){ url description } } 



I.e. nós apenas chamamos o método pelo nome e passamos parâmetros para ele. Você também pode trair as informações que queremos recuperar.

Como ver uma solicitação de mutação
1. Abra a guia do desenvolvedor F12 e abra a rede, enviamos a solicitação.



2. Na solicitação enviada, RMB -> copiar -> copiar como cURL (bash)



2.1 Para quem usa curl, basta, para quem quer ver o carteiro ir além

3. Abra o carteiro e clique em importar no canto superior esquerdo.



4. Na janela que se abre, selecione Colar texto não processado e cole a solicitação de ondulação copiada lá



E podemos ver o corpo da solicitação:

 {"query":"mutation createLink{\n createLink(url:\"http://test.com\", description:\"test mutation\"){\n\t\turl\n description\n }\n}","variables":null,"operationName":"createLink"}    “\n”. 


Nesse estágio, já existe um servidor simples, há armazenamento e definimos o URL para solicitações.

Gostei da ideia em si. A frente em si pode determinar quais informações são necessárias atualmente, como economizar tráfego. Ao adicionar novas funcionalidades, a antiga continua funcionando, a frente continua recebendo apenas o que precisa, nem mais nem menos.

Código de exemplo do Github
2 ramos:

master - como é feito no tutorial oficial

update_version é uma versão atualizada, com novas versões de dependências.

Referências:

1. Docas
2. tutorial oficial (para diferentes idiomas)
3. O vídeo que deu o primeiro entendimento

Source: https://habr.com/ru/post/pt418083/


All Articles