GraphQL + NET

Flavio Ribeiro Lima
5 min readMar 14, 2023

--

O que é GraphQL?

GraphQL é uma linguagem de consulta (query language) para APIs. Ele é também um runtime para atender consultas de dados. O serviço GraphQL é independente de transporte, mas em geral é servido sobre HTTP.

O GraphQL foi desenvolvido em 2012 pelo Facebook. A história conta que o Facebook utilizava RESTful e estava enfrentando problemas de desempenho, e com frequência apresentavam falhas. Foi então que a equipe de engenheiros perceberam que precisavam melhorar o modo como os dados estavam sendo enviados.

Mas afinal qual problema o GraphQL se propõe a resolver?

Overfetching

Com o GraphQL você não tem o problema de overfeching, que é receber mais dados do que precisa, (uma busca de dados em excesso).

Ao consumir um endpoint em REST, independente do que irá precisar, seja apenas o nome e o id do cliente, por ex. você sempre irá receber todos os dados que aquele endpoint disponibiliza. Para resolver isso, você teria que criar um série de endpoints para cada necessidade especifica, o que pode gerar outros problemas.

Com GraphQL, você tem apenas um endpoint, e pode solicitar apenas os dados que precisa naquele momento.

Desvantagem

Uma desvantagem que vejo no uso do GraphQL é que ele sempre responde status 200, seja uma query ou uma mutation. Então você perde o poder da validação do status code.

E a beleza da invocação de endpoints por verbos, como no RESTful, se perde, por que no GraphQL tudo é POST.

Implementando uma API GraphQL com .NET

Primeiro passo será definir a estrutura básica de um projeto, então vamos começar pela entidade. Será um exemplo simples, o foco será a implementação do GraphQL.

Entidade

public class Cliente
{
[Key]
public int Id { get; private set; }
public string Nome { get; private set; }
public string Sobrenome { get; private set; }
public DateTime DataNascimento { get; private set; }
public bool Ativo { get; private set; }

public Cliente(string nome, string sobrenome, DateTime dataNascimento, bool ativo)
{
Nome = nome;
Sobrenome = sobrenome;
DataNascimento = dataNascimento;
Ativo = ativo;
}
}

Context

public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options)
: base(options) { }

public AppDbContext() { }

public DbSet<Cliente> Clientes { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();

var connectionString = configuration.
GetConnectionString("DefaultConnection");
optionsBuilder.UseSqlServer(connectionString);
}
}
}

Repository Interface

public interface IClienteRepository
{
IEnumerable<Cliente> GetAll();
Cliente Get(int id);
void Add(Cliente cliente);
}

Repository

public class ClienteRepository : IClienteRepository
{
private readonly AppDbContext _context;
public ClienteRepository(AppDbContext context)
{
_context = context;
}

public void Add(Cliente cliente)
{
using (_context)
{
_context.Clientes.Add(cliente);
_context.SaveChanges();
}
}

public Cliente Get(int id) => _context.Clientes.FirstOrDefault(x => x.Id == id);

public IEnumerable<Cliente> GetAll() => _context.Clientes.ToList();
}

Com essas peças básicas prontas, vamos definir os tipos do GraphQL. Lembre-se que a estrutura acima está simples de forma intencional, em uma modelagem real, as coisas são um “pouco” diferentes.

Antes de seguir instale os pacotes do GraphQL.Net

Para facilitar segue…

<PackageReference Include="graphiql" Version="2.0.0" />
<PackageReference Include="GraphQL" Version="7.3.0" />
<PackageReference Include="GraphQL.Server.Transports.AspNetCore" Version="7.3.0" />
<PackageReference Include="GraphQL.Server.Ui.Playground" Version="7.3.0" />

Feito isso oprimeiro passo é definir o ClienteType que será o nosso tipo GraphQL para representar os dados.

public class ClienteType : ObjectGraphType<Cliente>
{
public ClienteType()
{
Name = "clientes";

Field(x => x.Id, type: typeof(IdGraphType))
.Description("Propriedade Id do objeto cliente.");

Field(x => x.Nome, type: typeof(StringGraphType))
.Description("Propriedade Nome do objeto cliente.");

Field(x => x.Sobrenome, type: typeof(StringGraphType))
.Description("Propriedade Sobrenome do objeto cliente.");

Field(x => x.DataNascimento, type: typeof(DateGraphType))
.Description("Propriedade DataNascimento do objeto cliente.");

Field(x => x.Ativo, type: typeof(BooleanGraphType))
.Description("Propriedade Ativo do objeto cliente.");
}
}

Vamos agora criar nossa classe para permitir a execução de queries.

public class ClienteQuery : ObjectGraphType<object>
{
public ClienteQuery(IClienteRepository repository)
{
Name = "ClienteQuery";

Field<ListGraphType<ClienteType>>(
"clientes",
resolve: context => repository.GetAll());

Field<ClienteType>(
"cliente",
arguments: new QueryArguments(new QueryArgument<IntGraphType> { Name = "id" }),
resolve: context => {
return repository.Get(context.GetArgument<int>("id"));
});
}
}

Abaixo segue a implementação da mutation para a realização de comandos de alteração.

public class ClienteMutation : ObjectGraphType
{
public ClienteMutation(IClienteRepository repository)
{
Name = "ClienteMutation";

Field<ClienteType>(
"cadastrarcliente",
arguments: new QueryArguments(
new QueryArgument<StringGraphType> { Name = "nome" },
new QueryArgument<StringGraphType> { Name = "sobrenome" },
new QueryArgument<DateGraphType> { Name = "datanascimento" },
new QueryArgument<BooleanGraphType> { Name = "ativo" }
),
resolve: context =>
{
var cliente = new Cliente(context.GetArgument<string>("nome"),
context.GetArgument<string>("sobrenome"),
context.GetArgument<DateTime>("datanascimento"),
context.GetArgument<bool>("ativo")
);

repository.Add(cliente);
return cliente;
});
}
}

Por fim definimos o Schema.

public class AppScheme : Schema
{
public AppScheme(IDependencyResolver resolver) : base(resolver)
{
Query = resolver.Resolve<ClienteQuery>();
Mutation = resolver.Resolve<ClienteMutation>();
}
}

Agora vamos configurar nossa classe Startup.cs

public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AppDbContext>();

services.AddScoped<IClienteRepository, ClienteRepository>();

services.AddScoped<IDependencyResolver>(s => new FuncDependencyResolver(s.GetRequiredService));
services.AddScoped<AppScheme>();
services.AddGraphQL(o => { o.ExposeExceptions = false; }).AddGraphTypes(ServiceLifetime.Scoped);

services.Configure<IISServerOptions>(options =>
{
options.AllowSynchronousIO = true;
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}

app.UseHttpsRedirection();
app.UseGraphiQl();
app.UseGraphQL<AppScheme>();
}

Você pode adicionar no launchSettings.json

"GraphQL": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "graphql",
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}

Basta executar o projeto e testar com o playground.

Mutation

Para realizar alguns testes, execute a mutation abaixo. O cadastro será realizado e irá retornar o id gerado.

mutation{
cadastrarcliente(nome:"James", sobrenome:"Hetfield", ativo:true, datanascimento:"1963/08/03")
{
id
}
}

Query

Execute as queries abaixo para ver os resultado. Selecionando todos os clientes.

{
clientes {
id,
nome,
sobrenome
}
}

Ou selecionando um cliente especifico.

{
cliente(id: 11) {
id
nome
sobrenome
}
}

Para mais informações sobre a construção de queries, mutation, types, schema e mais acesse https://graphql.org/

Muito obrigado! Até a próxima.

--

--

Flavio Ribeiro Lima

Desenvolvedor de Software. Entusiasta de boas práticas, padrões de desenvolvimento e arquitetura de sistemas.