สำหรับ Blog นี้ เรียกว่าลองทำ GraphQL ด้วย dotnet core 8 ครับ โดยใช้ library HotChocolate ชื่อมันดูน่ากินดีนะ 555 โดยของหวาน Set นี้มี 3 Lib
- Hot Chocolate - open-source GraphQL server
- Strawberry Shake - open-source GraphQL client
- Banana Cake Pop - GraphQL GUI
สำหรับ Blog ช่วงนี้จะเน้นตัว Hot Chocolate ก่อนครับ แล้วค่อยขยับไปส่วนอื่นๆ โดยมีหัวข้อดังนี้
มาเริ่มกันเลย
- Simple REST API (GET)
dotnet new webapi --use-controllers -o GraphQLAPI
- เพิ่ม Library เข้าไป
- HotChocolate.AspNetCore ตัว Engine หลักเลยที่จัดการเรื่องนี้
- HotChocolate.AspNetCore.Playground ตัวสร้าง Web คล้าย Postman ให้เราลอง Test ส่ง Request ได้ - คำสั่งที่ใช้เพิ่ม Library
dotnet add package HotChocolate.AspNetCore dotnet add package HotChocolate.AspNetCore.Playground
- มาวางโครงสร้าง Project กันก่อน จะเป็นตามนี้
GraphQLAPI -> Controllers -> GraphQL ---> Query ---> Type -> Services -> Infra ---> Models ---> Repositories
- มาที่ส่วน Infra > Model กันก่อน เราจะลองมาเพิ่มส่วน DTO กันก่อน เอาไว้เก็บข้อมูล ในที่นี้ผมใช้ข้อมูล Suppliers นะ (จริงๆ Copilot) มันแนะนำมา ผมเลยตามเลยครับ
namespace GraphQLAPI.Infra.Model { public class SupplierDTO { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Address { get; set; } public string Email { get; set; } public string Phone { get; set; } } }
- ต่อมาเพิ่มส่วนของ Infra > Repository เพิ่ม SupplierRepository ขึ้นมาจัดการ ตอนนี้ผมจะเน้นส่วนยการดึงข้อมูลก่อน เลยเพิ่ม Operation ดึงข้อมูล และให้คืนจากที่ mock ไว้ไปก่อน พวก Add /Edit เดี๋ยวต่อยมาต่อยอดอีก Blog
using GraphQLAPI.Infra.Model; namespace GraphQLAPI.Infra.Repository { public interface ISupplierRepository { Task<IEnumerable<SupplierDTO>> GetSuppliers(); Task<SupplierDTO> GetSupplier(int id); // Task<SupplierDTO> AddSupplier(Supplier supplier); // Task<SupplierDTO> UpdateSupplier(Supplier supplier); // Task<SupplierDTO> DeleteSupplier(int id); } }
namespace GraphQLAPI.Infra.Repository; using GraphQLAPI.Infra.Model; using System.Collections.Generic; public class SupplierRepository: ISupplierRepository { private readonly IEnumerable <SupplierDTO> suppliers = new List <SupplierDTO> { new SupplierDTO { Id = 1, FirstName = "PingkungA", LastName = "Agnukginp", Address = "Thailand, BKK", Phone = "082-123-4569", Email = "pingkunga@debuggingsoft.com" }, new SupplierDTO { Id = 2, FirstName = "Faii", LastName = "Foster", Address = "Thailand, Chiangrai", Phone = "082-123-4561", Email = "abc@Foster.com" }, new SupplierDTO { Id = 3, FirstName = "Steve", LastName = "Kook", Address = "HK, Kowloon", Phone = "9999999999", Email = "xyz@abc.com" } }; public async Task <IEnumerable <SupplierDTO>> GetSuppliers() { return await Task.FromResult(suppliers); } public async Task <SupplierDTO> GetSupplier(int Id) { return await Task.FromResult(suppliers.FirstOrDefault(x => x.Id == Id)); } }
- ต่อมาเพิ่มในส่วนชั้น Service ครับ ล้อกันครับ โดยมีตัว ISupplierService / SupplierService Logic การทำงานตามนี้เลยครับ
namespace GraphQLAPI.Services; using GraphQLAPI.Infra.Models; public interface ISupplierService { Task<IEnumerable<SupplierDTO>> GetSuppliers(); Task<SupplierDTO> GetSupplier(int id); // Task<SupplierDTO> AddSupplier(Supplier supplier); // Task<SupplierDTO> UpdateSupplier(Supplier supplier); // Task<SupplierDTO> DeleteSupplier(int id); }
namespace GraphQLAPI.Services; using GraphQLAPI.Infra.Models; using GraphQLAPI.Infra.Repositories; public class SupplierService : ISupplierService { private readonly ISupplierRepository _supplierRepository; public SupplierService(ISupplierRepository supplierRepository) { _supplierRepository = supplierRepository; } public async Task<IEnumerable<SupplierDTO>> GetSuppliers() { return await _supplierRepository.GetSuppliers(); } public async Task<SupplierDTO> GetSupplier(int id) { return await _supplierRepository.GetSupplier(id); } }
- Register Service / Repository ให้ตัว NET8 DI จัดการต่อ ใน program.cs ให้เพิ่ม Code ลงไป ดังนี้
//Repiository builder.Services.AddScoped<ISupplierRepository, SupplierRepository>(); //Service builder.Services.AddScoped<ISupplierService, SupplierService>();
- เพิ่มส่วน Controller
using Microsoft.AspNetCore.Mvc; using GraphQLAPI.Services; using GraphQLAPI.Infra.Models; namespace GraphQLAPI.Controllers; [Route("api/[controller]")] [ApiController] public class SupplierController : ControllerBase { private readonly ISupplierService _supplierService; public SupplierController(ISupplierService supplierService) { _supplierService = supplierService; } [HttpGet] public async Task<IEnumerable<SupplierDTO>> GetSuppliers() { return await _supplierService.GetSuppliers(); } [HttpGet("{id}")] public async Task<SupplierDTO> GetSupplier(int id) { return await _supplierService.GetSupplier(id); } }
ตอนนี้ API ของเราพร้อมใช้งานแล้วครับ
-เพิ่มในส่วนของ GraphQL
จาก Blog เดิม จดจาก Build GraphQL APIs (Go) พบว่าส่วนประกอบของ GraphQL มี Type / Field (method) / Argument(method parameter) / Resolver (Field Resolver)
- ส่วนแรกมาที่ GraphQL > Type มา Mapping ระหว่าง Data Model กับ GraphQL Type
using GraphQLAPI.Infra.Model; namespace GraphQLAPI.GraphQL.Type { public class SupplierType : ObjectType<SupplierDTO> { protected override void Configure(IObjectTypeDescriptor <SupplierDTO> descriptor) { descriptor.Field(a => a.Id).Type<IdType>(); descriptor.Field(a => a.FirstName).Type<StringType>(); descriptor.Field(a => a.LastName).Type<StringType>(); descriptor.Field(a => a.Address).Type<StringType>(); descriptor.Field(a => a.Phone).Type<StringType>(); } } }
- ต่อ Map Field (method) / Argument(method parameter) และ Resolver (Field Resolver) โดยที่ Folder GraphQL > Query
using GraphQLAPI.Infra.Models; using GraphQLAPI.Infra.Repositories; using GraphQLAPI.Services; namespace GraphQLAPI.GraphQL.Query { public class SupplierGraphQLQuery { public async Task<IEnumerable<SupplierDTO>> GetAllSuppliers([Service]ISupplierService supplierService) { IEnumerable<SupplierDTO> suppliers = await supplierService.GetSuppliers(); return suppliers; } public async Task<SupplierDTO> GetSupplierById([Service]ISupplierService supplierService, int id) { SupplierDTO supplier = await supplierService.GetSupplier(id); return supplier; } } }
- ต่อเพิ่มให้ GraphQL สร้าง Schema ออกมา
builder.Services.AddGraphQLServer() .AddType<SupplierType>() .AddQueryType<SupplierGraphQLQuery>();
- และเปิด Endpoints ให้เข้่าไปเล่นในส่วนของ Playground
app.UsePlayground(new PlaygroundOptions { QueryPath = "/graphql", Path = "/playground" }); app.MapGraphQL(); //for frontend
Test
- ลอง REST API แบบเดิมๆ
@GraphQLAPI_HostAddress = http://localhost:5126 ## Rest API ## ### Get Supplier By id ### GET {{GraphQLAPI_HostAddress}}/api/supplier ### Get all Supplier ### GET {{GraphQLAPI_HostAddress}}/api/supplier/2
- GraphQL API (Playground) <base_url>/playground
- GraphQL API (.http) - เห็นว่าต้องส่งเป็น Post ทุกรอบนะ และต้องกำหนด X-REQUEST-TYPE: GraphQL โดยที่มี body ตามข้อกำหนดของ GraphQL
@GraphQLAPI_HostAddress = http://localhost:5126 ### Get all Supplier ### POST {{GraphQLAPI_HostAddress}}/graphQL Content-Type: application/json X-REQUEST-TYPE: GraphQL query { allSuppliers{ id firstName lastName } } ### Get Supplier By id ### POST {{GraphQLAPI_HostAddress}}/graphQL Content-Type: application/json X-REQUEST-TYPE: GraphQL query { supplierById(id:2){ firstName address phone } }
เห็นไหมครับ จริงๆ Graph API ใช่ร่วมกับ REST API เดิมได้นะ
Code เต็มๆอยู่นี้ครับ : pingkunga/net8_graphql_HotChocolate_sample (github.com)
สำหรับ Blog หน้าจะไปในส่วน CREATE / UPDATE / DELETE ครับ
Discover more from naiwaen@DebuggingSoft
Subscribe to get the latest posts sent to your email.