GraphQL: facing complex types

Posted by 2005 on Wed, 02 Feb 2022 00:00:53 +0100

GraphQL is not only a query language for API, but also a runtime to meet your data query. GraphQL provides an easy to understand and complete description of the data in your API, so that the client can accurately obtain the data it needs without any redundancy. It also makes it easier for the API to evolve over time and can be used to build powerful developer tools.

-- from https://graphql.cn

In the last blog post, our return value is a string. For most cases, we mostly return the json format of the entity class.

first edition

using HotChocolate;
using HotChocolate.Data;
using HotChocolate.Execution;
using HotChocolate.Types;
using System;
using System.Collections.Generic;


namespace GraphQLBase002
{
    class Program
    {
        static void Main(string[] args)
       {         
            FirstVersion.Run();       
        }
    }
   //Entity class
    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }

    #region FirstVersion
    public class FirstVersion
    {
        public static void Run()
{
            var schema = SchemaBuilder.New()
                .AddQueryType<QueryType>()
                .Create();
            var executor = schema.MakeExecutable();
            //The return is a string, so the defined Resolver name is used to query
            Console.WriteLine(executor.Execute("{ students }").ToJson());
        }

        public class Query
        {
            public IList<Student> GetStudents()
            {
                return new List<Student>() {
                    new Student {
                        Id = 100,
                        Name = "ABCD",
                        Age=20
                    },
                     new Student {
                        Id = 101,
                        Name = "EFGH",
                        Age=19
                    }
                };
            }
        }

        public class QueryType : ObjectType<Query>
        {
            protected override void Configure(IObjectTypeDescriptor<Query> descriptor)
{
                //It is defined that there are students to request GetStudents method, and the return type is StringType, so the entity will be converted to Json in Resolver
                descriptor.Field<Query>(t => t.GetStudents()).Name("students").Type<NonNullType<StringType>>().Resolver(ctx =>
               {
                   var result = ctx.Parent<Query>().GetStudents();
                   return Newtonsoft.Json.JsonConvert.SerializeObject(result);
               });
            }
        }
    }
    #endregion

In order to return a json, use Resolver to get GetStudents and turn the instance into a json. Because it is a string, the Type of this Field is StringType.

The running result looks like a json, not exactly a string in json format. In fact, it is very clear from our definition of Resolver; This is not what we want.

 

 

Second Edition

 

 #region SecondVersion
    public class SecondVersion
    {
        public static void Run()
{
            var schema = SchemaBuilder.New()
                .AddQueryType<QueryType>()
                .Create();
            var executor = schema.MakeExecutable();

            Console.WriteLine(executor.Execute("{ student {id name} }").ToJson());
            Console.WriteLine(executor.Execute("{ students {id name} }").ToJson());
        }
        public class Query
        {

            public Student GetStudent()
{
                return new Student
                {
                    Id = 1,
                    Name = "AAAAA",
                    Age = 19

                };
            }

            public List<Student> GetStudents()
            {
                return new List<Student>{
                    new Student
                    {
                        Id = 100,
                        Name = "ABCD",
                        Age = 19
                    },
                    new Student
                    {
                        Id = 101,
                        Name = "EFGH",
                        Age = 20
                    }
                };
            }
        }

        public class StudentType : ObjectType<Student>
        {
            protected override void Configure(IObjectTypeDescriptor<Student> descriptor)
{
            }
        }

        public class QueryType : ObjectType<Query>
        {
            protected override void Configure(IObjectTypeDescriptor<Query> descriptor)
{
                descriptor.Field(t => t.GetStudent()).Type<NonNullType<StudentType>>().Name("student");
                descriptor.Field(t => t.GetStudents()).Type<ListType<NonNullType<StudentType>>>().Name("students");
            }
        }
    }
    #endregion

This time, in order to stop being a json format string, we defined the type of StudentType in the code, telling the system that Student is not a simple type, but the attributes in Student are simple types, Therefore, there is no processing in Configure (if there is a custom complex type attribute in Student, this type must be further defined and processed in Configure). In QueryType, the type definition and renaming of two methods in Query are processed.

The operation results are as follows. Yes, this is the result we want; But I always feel that in order to return json, is our price a little high?

 

 

Third Edition

#region ThreeVersion
    public class ThreeVersion
    {
        public static void Run()
        {
            var schema = SchemaBuilder.New()
                .AddProjections()
                .AddQueryType<Query>()
                .Create();
            var executor = schema.MakeExecutable();

            Console.WriteLine(executor.Execute("{ student{id name age} }").ToJson());
            Console.WriteLine(executor.Execute("{ students{id name age} }").ToJson());
        }
        public class Query
        {
            [UseProjection]            
            public Student GetStudent()
            {
                return new Student
                {
                    Id = 1,
                    Name = "AAAAA",
                    Age = 19

                };
            }
            [UseProjection]
            public List<Student> GetStudents()
            {
                return new List<Student>{
                    new Student
                    {
                        Id = 100,
                        Name = "ABCD",
                        Age = 19
                    },
                    new Student
                    {
                        Id = 101,
                        Name = "EFGH",
                        Age = 20
                    }
                };
            }
        }
    }
    #endregion

In this version, we use the chicken to lay eggs and use the UseProjection instead of the type we defined. Even the QueryType disappears. This kind of code is what we want. Let's pay more attention to the business logic rather than doing a lot of technical cooperation for GraphQL; In fact, from the second edition, we can see the defined types StudentType and QueryType. We also know that these types are very regular and can be replaced by code, that is, UseProjection.

The running result is the same as that of version 2.

 

You can pay more attention to WeChat official account if you want to know more quickly and conveniently.
 

 

 

 
 

Topics: .NET