1

I'm new to HotChocolate and GraphQL, and I have some difficulties with type extension.

Is it possible to extend type with ObjectType field? I've found only one example in the documentation with StringType:

protected override void Configure(IObjectTypeDescriptor descriptor)
{
    descriptor.Name("Person");
    descriptor.Field("address")
        .Type<StringType>()
        .Resolver("Address");
}

I've tried to do something similar, but I have this exception HotChocolate.SchemaException: Unable to resolve type reference Output: ObjectType.

protected override void Configure(IObjectTypeDescriptor descriptor)
{
    descriptor.Name("Person");
    descriptor.Field("address")
        .Type<ObjectType>()
        .Resolver(ctx => new ObjectType(d => d.Field("street").Type<StringType>().Resolver("Street")));
}

Could you please advice any methods to extend type with ObjectType field in my case? Or just answer whether it is possible or not?

Thanks!

  • The purpose of extending you make is actually not clear. Could you give more detailed information about the use case which you think causes that need? According to your example: it seems that more natural way is to define type Address and it's metadatata type (public class AddressType : ObjectType
    {...}).
    – ademchenko Mar 02 '21 at 09:02
  • One of the fields in my entity is a json object that is configured by the user in runtime. So I can't describe a suitable object in advance. But I would like to be able to show in the GraphQL schema the fields that this json contains. – Rival Abdrakhmanov Mar 02 '21 at 13:06
  • Schema is something that is set up prior to using. For example, HotChocolate v.11 compiles the schema on start, so, I'm not sure it's the correct way to change it on runtime. It might be more suitable to present JSON objects like dictionaries with property names as the keys. – ademchenko Mar 02 '21 at 14:23
  • Actually, I know the scheme by the time the application starts, and it doesn't change throughout its life. So I thought it would be useful to display this in the GraphQL schema. – Rival Abdrakhmanov Mar 03 '21 at 11:36

1 Answers1

0

Suppose, your class Book contains some JSON field 'Data'. The structure of 'Data' can be changed between restarts but is known prior to any start (i.e. you know both property names and their types at the startup). The following code addresses that case:

using System.Linq;
using HotChocolate.Resolvers;
using HotChocolate.Types;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json.Linq;

namespace Ademchenko.GraphQLWorkshop
{
    public class Book
    {
        public int Id { get; set; }

        public JObject Data { get; set; }
    }

    public interface IBookService { IQueryable<Book> GetAll(); }

    public class InMemoryBookService : IBookService
    {
        private readonly Book[] _staticBooks = {
            new Book {Id = 11, Data = JObject.FromObject(new {Title = "FooBook", AuthorId = 1, Price = 10.2m})},
            new Book {Id = 22, Data = JObject.FromObject(new {  Title = "BarBook", AuthorId = 2, Price = 20.2m})}
        };

        public IQueryable<Book> GetAll() => _staticBooks.AsQueryable();
    }

    public class Query
    {
        public IQueryable<Book> GetBooks(IResolverContext ctx) => ctx.Service<IBookService>().GetAll();
    }

    public class BookType : ObjectType<Book>
    {
        protected override void Configure(IObjectTypeDescriptor<Book> descriptor)
        {
            descriptor.Field(d => d.Data).Type<DataType>();
        }
    }

    public class DataType : ObjectType
    {
        protected override void Configure(IObjectTypeDescriptor descriptor)
        {
            descriptor.Field("title").Type<StringType>().Resolve((ctx, ct) => (string)ctx.Parent<JObject>()["Title"]);
            descriptor.Field("authorId").Type<IntType>().Resolve((ctx, ct) => (int)ctx.Parent<JObject>()["AuthorId"]);
            descriptor.Field("price").Type<DecimalType>().Resolve((ctx, ct) => (decimal)ctx.Parent<JObject>()["AuthorId"]);
        }
    }

    public class Startup
    {
        public IConfiguration Configuration { get; }

        public Startup(IConfiguration configuration) => Configuration = configuration;

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();

            services.AddSingleton<IBookService, InMemoryBookService>();

            services.AddGraphQLServer()
                .AddQueryType<Query>()
                .AddType<BookType>();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env) => app.UseRouting().UseEndpoints(endpoints => endpoints.MapGraphQL());
    }
}

Making the request to the server:

{
  books
  {
    id,
    data
    {
      title
      authorId
      price
    }
  } 
}

you will get the following response:

{
  "data": {
    "books": [
      {
        "id": 11,
        "data": {
          "title": "FooBook",
          "authorId": 1,
          "price": 1
        }
      },
      {
        "id": 22,
        "data": {
          "title": "BarBook",
          "authorId": 2,
          "price": 2
        }
      }
    ]
  }
}
ademchenko
  • 585
  • 5
  • 18
  • Thank you! But what if I have not only simple types (like `StringType`, `IntType`, `DecimalType`), but also other objects with the unknown structure inside the `DataType`? – Rival Abdrakhmanov Mar 09 '21 at 06:33