.NET

13 nov, 2018

Construindo uma API GraphQL com ASP .NET Core e Entity Framework Core

Publicidade

Introdução

Dando continuidade ao meu artigo anterior “Introdução ao GraphQL“, hoje irei para parte prática. O objetivo desse artigo é demonstrar como podemos trabalhar com GraphQL, .NET Core e o Entity Framework Core.

Caso esse seja o seu primeiro contato com GraphQL, saiba que ele é uma linguagem de consulta desenvolvida pelo Facebook em 2012, e três anos depois (2015) em um evento sobre React, foi liberado para a comunidade.

Mas aí nos perguntamos: Por que utilizar ele? Qual problema ele veio resolver?

Para responder essas e outras perguntas, vamos a uma pequena comparação entre o padrão REST (um dos mais utilizados hoje) e o GraphQL.

+-----------+--------------------+---------------------------------+
|            REST                |     GraphQL                     | 
+--------------------------------+---------------------------------+
| Requisição a mais de uma rota     | Somente uma rota             | 
| API's grandes tem muitos endpoints| Somente um endpoint          |
| Tráfego desnecessário de dados | cliente solicita só o necessário| 
+-----------+--------------------+---------------------------------+

Eu abordei os três pontos que o pessoal mais comenta sobre REST x GraphQL. Caso queira saber mais sobre GraphQL, eu recomendo a leitura deste link: Documentação oficial GraphQL.

Configuração do Projeto

Para pular a etapa de criação de um novo projeto, eu irei utilizar um desenvolvido pelo Renato Groffe. Caso você tenha interesse em clonar ele também, segue link do projeto no GitHub do Renato: ASPNETCore2_CRUD-API-JWT-EFInMemory.

Com o projeto clonado, vamos adicionar as referências necessárias para trabalhar com GraphQL. Para isso, execute os comandos abaixo no seu terminal.

dotnet add package GraphQL 
dotnet add package graphiql

O próximo passo será criar uma Type para a nossa entidade Produto. Basicamente, as types são os mapeamentos das nossas entidades com o GraphQL

Crie um novo diretório dentro de Models chamado GraphQL, e dentro dele uma classe chamada ProdutoType. Em seguida, atualize-o com o trecho de código abaixo:

public class ProdutoType : ObjectGraphType<Produto>
    {
        public ProdutoType()
        {
            Name = "Produto";
            
            Field(x => x.CodigoBarras).Description("Código de Barras");

            Field(x => x.Nome).Description("Nome do produto");

            Field(x => x.Preco).Description("Preço");

        }
    }

Analisando o trecho de código acima, nós temos:

  • 01: estamos passando qual é a entidade que será mapeada para o nosso Type
  • 05: nome do type para pesquisas
  • 07 à 11: os campos do type com uma breve descrição de cada um deles

Com o type criado nós precisamos criar dois novos arquivos: um para as nossas queries chamado EatMoreQuery.cs e outro chamado GraphQLQuery.cs para os argumentos da nossa pesquisa.

Crie um novo diretório chamado Queries, e dentro dele os dois arquivos mencionados acima: EatMoreQuery.cs e GraphQLQuery.cs. Em seguida atualize-os com os trechos de código abaixo:

GraphQLQuery.cs

    public class GraphQLQuery
    {
        public string OperationName { get; set; }
        public string NamedQuery { get; set; }
        public string Query { get; set; }
        public JObject Variables { get; set; }
    }

EatMoreQuery.cs

 public class EatMoreQuery : ObjectGraphType
    {
        public EatMoreQuery(ApplicationDbContext db)
        {

            Field<ListGraphType<ProdutoType>>(
                "produtos",
                resolve: context =>
                {
                    var produtos = db
                    .Produtos;
                    return produtos;
                });

        }
    }

Analisando o trecho de código acima, temos:

  • 01: instância da classe ObjectGraphType
  • 02: construtor recebendo o Contexto do Entity Framework Core
  • 06: estamos passando que o nosso método retornará uma lista de objetos do tipo ProdutoType
  • 07: nome que usaremos para nossa chamada
  • 08: resolve com o contexto do nosso banco de dados. Aqui mapearemos a requisição do nosso cliente com os dados do nosso banco de dados. Como db é o contexto do Entity Framework, podemos acessar a partir dele as entidades mapeadas do nosso banco

O próximo passo será criar um endpoint para as nossas pesquisas. Para isso, crie uma nova Controller chamada GraphQLController e atualize-a com o trecho de código abaixo:

[Route("graphql")]
    public class GraphQLController : Controller
    {

        private readonly ApplicationDbContext _db;

        public GraphQLController(ApplicationDbContext db)
        {
            _db = db;
        }

        public async Task<IActionResult> Post([FromBody]GraphQLQuery query)
        {
            var inputs = query.Variables.ToInputs();

            var schema = new Schema()
            {
                Query = new EatMoreQuery(_db)
            };

            var result = await new DocumentExecuter().ExecuteAsync(_ =>
            {
                _.Schema = schema;
                _.Query = query.Query;
                _.OperationName = query.OperationName;
                _.Inputs = inputs;
            }).ConfigureAwait(false);

            if (result.Errors?.Count > 0)
            {
                return BadRequest();
            }

            return Ok(result);
        }
    }

Analisando o trecho de código acima, nós temos:

  • 05 a 10: estamos injetando DbContext na nossa controller
  • 14: estamos recebendo os valores da pesquisa
  • 16 a 19: estamos passando o contexto para os Schemas do GraphQL
  • 37 a 40: estamos passando os parâmetros da nossa pesquisa

Agora, para termos uma carga inicial para utilizar nas nossas pesquisas, abra o arquivo Startup.cs e atualize o método Configure com o trecho de código abaixo:

public void Configure(IApplicationBuilder app, IHostingEnvironment env,
            UsuarioService usrService, ProdutoService produtoService)
        {
            usrService.Incluir(
                new Usuario() { ID = "usuario01", ChaveAcesso = "94be650011cf412ca906fc335f615cdc" });

            usrService.Incluir(
                new Usuario() { ID = "usuario02", ChaveAcesso = "531fd5b19d58438da0fd9afface43b3c" });

            produtoService.Incluir(
                new Produto { CodigoBarras = "11111111111", Nome = "Produto01", Preco = 579 }
                );

            produtoService.Incluir(
              new Produto { CodigoBarras = "2222222222", Nome = "Produto02", Preco = 579 }
              );

            produtoService.Incluir(
              new Produto { CodigoBarras = "333333333333", Nome = "Produto03", Preco = 579 }
              );

            produtoService.Incluir(
              new Produto { CodigoBarras = "4444444444", Nome = "Produto04", Preco = 579 }
              );

            produtoService.Incluir(
              new Produto { CodigoBarras = "5555555555", Nome = "Produto05", Preco = 579 }
              );

            produtoService.Incluir(
              new Produto { CodigoBarras = "66666666666", Nome = "Produto06", Preco = 579 }
              );

            produtoService.Incluir(
              new Produto { CodigoBarras = "77777777777", Nome = "Produto07", Preco = 579 }
              );

            produtoService.Incluir(
              new Produto { CodigoBarras = "888888888888", Nome = "Produto08", Preco = 579 }
              );

            produtoService.Incluir(
              new Produto { CodigoBarras = "99999999999", Nome = "Produto09", Preco = 579 }
              );

            produtoService.Incluir(
              new Produto { CodigoBarras = "10101010101010101010", Nome = "Produto10", Preco = 579 }
              );

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseMvc();
        }

Testando o projeto

Para validar os passos anteriores eu irei utilizar o Postman, mas você pode utilizar uma outra ferramenta de testes HTTP de sua preferência.

Com o projeto rodando, abra sua ferramenta de testes HTTP e configure ela conforme na imagem abaixo:

Testando API GraphQL no Postman

A pesquisa anterior está simples. Caso execute, ela notará que retornaram todos os nomes que foram inseridos na nossa tabela em Memory. Caso queira trazer também o código de barras e o preço dos produtos, basta adicionar eles depois de nome entre virgulas. Abaixo você tem uma imagem demonstrando esse passo:

Bem simples, né? Com isso conseguimos resolver os três pontos que passei acima: trafego desnecessário de dados, centralização de todas as rotas em uma e versão das rotas.

O intuito deste artigo foi demonstrar de uma maneira simples como podemos trabalhar com GraphQL em um projeto .NET Core. Caso tenha interesse em baixar a versão final deste projeto, segue o link no meu GitHub:

Espero que tenham gostado e até um próximo artigo, pessoal!