0

I am building a small proof-of-concept project to see if we can use GrapQl on our services. The schema stitching feature looks really good and if we implement GraphQl, would really need it. After looking at the docs and running their example locally I felt confident enough to implement my own solution that mirrors more closely to our domain.

Here's the implementation:

// Program.cs
  var builder = WebApplication.CreateBuilder(args);
  builder.Services.AddDbContext<PeopleDbContext>(t=> t.UseInMemoryDatabase("PeopleDb"));
  builder.Services.AddGraphQLServer()
      .AddQueryType<PeopleQuery>()
      .RegisterDbContext<PeopleDbContext>();
  
  var app = builder.Build();
  app.MapGraphQL();
  app.Run();
  
  // PeopleQuery.cs
  public class PeopleQuery
  {
      public Person? GetPerson([FromServices] PeopleDbContext dbContext, int id) =>
          dbContext.People.FirstOrDefault(t => t.Id == id);
  }
  
  // Person.cs
  public class Person
  {
      public int Id { get; set; }
      public string FirstName { get; set; }
      public string LastName { get; set; }
      public DateTime Birthdate { get; set; }

  }
  • Employees
// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<EmployeesDbContext>(t =>  t.UseInMemoryDatabase("EmployeesDb"));
builder.Services.AddHttpClient("people", t=> t.BaseAddress = new Uri("https://localhost:8080/graphql"));
builder.Services.AddHttpClient("companies", t=> t.BaseAddress = new Uri("https://localhost:9090/graphql"));

builder.Services.AddGraphQLServer()
    .AddQueryType<EmployeesQuery>()
    .AddRemoteSchema(SchemaNames.Companies,ignoreRootTypes:true)
    .AddRemoteSchema(SchemaNames.Users, ignoreRootTypes:true)
    .AddTypeExtensionsFromFile("./Stitching.graphql")
    .RegisterDbContext<EmployeesDbContext>();

var app = builder.Build();
app.MapGraphQL();
app.Run();

// EmployeesQuery.cs
public class EmployeesQuery {
      public Employee? GetEmployee([Service] HrDbContext dbContext, int id) =>
        dbContext.Employees.FirstOrDefault(t=> t.Id == id);
}

// Employee.cs
public class Employee
{
    public int Id { get; set; }
    public int PersonId { get; set; }
    public int CompanyId { get; set; }
}

// Stitching.graphql
extend type Employee {
    person: Person @delegate(schema: "people", path: "person(id: $fields:personId)")
    company: Company @delegate(schema: "companies", path: "company(id: $fields:companyId)")
}
  • Companies
// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<CompanyDbContext>(t => t.UseInMemoryDatabase("CompanyDb"));
builder.Services.AddGraphQLServer()
    .AddQueryType<CompaniesQuery>()
    .RegisterDbContext<CompanyDbContext>();

var app = builder.Build();
app.MapGraphQL();
app.Run();

// CompaniesQuery.cs
public class CompaniesQuery
{
    public Company? GetCompany([FromServices] CompanyDbContext dbContext, int id) =>
        dbContext.Companies.FirstOrDefault(t=> t.Id == id);
}

// Company.cs
public class Company
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string CatchPhrase { get; set; }
}

All of this is using HotChocolate v12.14 and .NET 6.

If I run a simple query:

{
  employee(id: 2) {
    id
    personId
    person {
      firstName
    } 
  }
}

I get the following error, which means that I did not provide some variable:

"errors": [
    {
      "message": "Variable `__fields_personId` is required.",
      "extensions": {
        "code": "HC0018",
        "variable": "__fields_personId",
        "remote": {
          "message": "Variable `__fields_personId` is required.",
          "extensions": {
            "code": "HC0018",
            "variable": "__fields_personId"
          }
        },
        "schemaName": "people"
      }
    }
  ]

As far as I understand, when I extend the type Employee and I use $fields:personId, it is referring to the field personId in the instance of the Employee.

If in the file Stitching.graphql I hardcode a value for the parameters (instead of the fields reference), the request is successful and it returns the expected result:

extend type Employee {
    person: Person @delegate(schema: "people", path: "person(id: 1)")
    company: Company @delegate(schema: "companies", path: "company(id: 1)")
}

I found this question but it has no replies.

Lucas
  • 11
  • 2

1 Answers1

0

After some time working on the samples I managed to find my error. My mistake was to set up the GraphQL gateway on one of the services that were also providing it (in this case, the Employees service).

My solution for this issue was to create a new gateway service (using YARP, but you can use anything) that registered the remote schemas. With this new setup, I had schema stitching working perfectly fine for a few months now!

On the gateway service you can set it up like the following:

// Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpClient("employees", t=> t.BaseAddress = new Uri("https://localhost:9070/graphql"));
builder.Services.AddHttpClient("people", t=> t.BaseAddress = new Uri("https://localhost:8080/graphql"));
builder.Services.AddHttpClient("companies", t=> t.BaseAddress = new Uri("https://localhost:9090/graphql"));


builder.Services.AddGraphQLServer()
    .AddRemoteSchema(SchemaNames.Companies,ignoreRootTypes:true)
    .AddRemoteSchema(SchemaNames.Users, ignoreRootTypes:true)
    .AddRemoteSchema(SchemaNames.Employees,ignoreRootTypes:true)
;

var app = builder.Build();
app.MapGraphQL();
app.Run();
Lucas
  • 11
  • 2