Sharing is caring!

สารบัญ

  1. WebFlux + GraphQL คืออะไร
  2. ทำไมต้องจับคู่สองเทคโนโลยีนี้
  3. สแตกเทคโนโลยีและ dependency
  4. ออกแบบ Schema GraphQL
  5. เขียน Resolver แบบ Reactive
  6. Router Functions แบบ Non-Blocking
  7. GraphQL Subscription ผ่าน WebSocket
  8. Security & Validation
  9. Unit Test และ Integration Test
  10. Logging & Tracing ที่เหมาะสม
  11. Deploy บน Kubernetes
  12. Best Practices
  13. สรุป

1. WebFlux + GraphQL คืออะไร

Spring WebFlux คือ framework แบบ Reactive, Non-Blocking ที่ใช้ Project Reactor เป็นหัวใจ — ส่วน GraphQL คือ query language ที่ให้ client ดึงข้อมูลตามโครงสร้างที่ต้องการพลิกแพลงได้ เมื่อรวมสองสิ่งเข้าด้วยกัน เราจะได้ API ที่ทั้ง scalable และ flexible พร้อม real-time subscription ในตัว

2. ทำไมต้องจับคู่สองเทคโนโลยีนี้

  • Back-pressure — WebFlux รับ load สูงได้ด้วย thread น้อย
  • Over/Under-fetching = 0 — GraphQL ให้ client เลือก field เอง
  • Streaming — Combine GraphQL Subscription กับ reactor Flux
  • Single Endpoint — ลดปัญหาจัดการ version REST

3. สแตกเทคโนโลยีและ dependency

  • Spring Boot 3.5.x
  • spring-boot-starter-webflux
  • spring-boot-starter-graphql (GraphQL Java 19.x)
  • reactor-test สำหรับ unit test
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-graphql</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
  

4. ออกแบบ Schema GraphQL

type Query {
  book(id: ID!): Book
  books: [Book]
}

type Mutation {
  addBook(input: BookInput!): Book
}

type Subscription {
  bookAdded: Book
}

type Book {
  id: ID!
  title: String!
  author: String!
}

input BookInput {
  title: String!
  author: String!
}
  

5. เขียน Resolver แบบ Reactive

@Component
@RequiredArgsConstructor
public class BookResolver {

  private final BookRepository repo;
  private final Sinks.Many<Book> sink =
      Sinks.many().multicast().onBackpressureBuffer();

  @QueryMapping
  public Mono<Book> book(@Argument String id) {
    return repo.findById(id);          // Return Mono
  }

  @QueryMapping
  public Flux<Book> books() {
    return repo.findAll();             // Return Flux
  }

  @MutationMapping
  public Mono<Book> addBook(@Argument BookInput input) {
    return repo.save(new Book(null, input.title(), input.author()))
               .doOnNext(sink::tryEmitNext);
  }

  @SubscriptionMapping
  public Flux<Book> bookAdded() {
    return sink.asFlux();
  }
}
  

6. Router Functions แบบ Non-Blocking

@Bean
RouterFunction<ServerResponse> graphiQL() {
  return RouterFunctions.route()
      .GET("/", req -> ServerResponse.temporaryRedirect(URI.create("/graphiql.html")).build())
      .build();
}
  
โฟลว์ WebFlux Handler ส่ง Request เข้า GraphQL Engine แบบ Non-Blocking

7. GraphQL Subscription ผ่าน WebSocket

Spring GraphQL ใช้ /graphql endpoint เดียวรองรับทั้ง HTTP POST และ graphql-ws protocol (Apollo). เปิด SockJS ได้ด้วย:

@Configuration
@EnableWebSocket
class GraphQLWsConfig implements WebSocketConfigurer {
  public void registerWebSocketHandlers(WebSocketHandlerRegistry reg) {
    reg.addHandler(GraphQlWebSocketHandler.create("/graphql"), "/graphql")
       .setAllowedOrigins("*").withSockJS();
  }
}
  

Frontend:

import { createClient } from "graphql-ws";
const client = createClient({ url: "ws://localhost:8080/graphql" });
client.subscribe(
  { query: "subscription{bookAdded{title}}" },
  { next: data => console.log(data) }
);
  

8. Security & Validation

  • ใช้ MaxQueryDepthInstrumentation ป้องกัน query ลึกเกิน
  • เปิด graphiql เฉพาะ profile dev
  • ใส่ JWT ด้วย Spring Security Reactive + DataLoaderContext

9. Unit Test และ Integration Test

@WebFluxTest(BookResolver.class)
class BookResolverTest {

  @Autowired
  private WebTestClient graphQlClient;

  @Test
  void shouldReturnBook() {
    graphQlClient.post()
        .uri("/graphql")
        .bodyValue("""
          {"query":"{ book(id:\\"1\\"){title}}" }
        """)
        .exchange()
        .expectStatus().isOk()
        .expectBody()
        .jsonPath("$.data.book.title").isEqualTo("Reactive Spring");
  }
}
  

10. Logging & Tracing ที่เหมาะสม

ใส่ graphql.execution logging level DEBUG ชั่วคราวเพื่อดู timing, ใช้ micrometer-tracing ร่วมกับ reactor-context คง traceId ตลอด chain.

11. Deploy บน Kubernetes

livenessProbe:
  httpGet: { path: /actuator/health/liveness, port: 8080 }
readinessProbe:
  httpGet: { path: /actuator/health/readiness, port: 8080 }
env:
- name: SPRING_GRAPHQL_GRAPHIQL_ENABLED
  value: "false"
  

เปิด HPA scale pod ตาม CPU/Memory และ request_per_second metric จาก NGINX Ingress

12. Best Practices

  1. ใช้ DataLoader รวม batch SQL ภายใน resolver
  2. แยก DTO ออกจาก entity เพื่อลด field leakage
  3. ตั้ง Timeout + CircuitBreaker รอบ call ไป service อื่น
  4. ใช้ contract-test GraphQL (eg. Spectral) เช็ก schema drift

13. สรุป

การผสาน WebFlux กับ GraphQL ทำให้ได้ API ที่กระทัดรัด Reactive และ real-time ใน endpoint เดียว เพียงเตรียม schema, wiring resolver ให้คืน Mono/Flux และดูแล security, logging, deployment ให้ครบถ้วน — คุณก็พร้อมเสิร์ฟข้อมูลให้ client แบบ flexible และ scalable ทันที 🚀


© 2025 poolsawat.com • หากบทความนี้มีประโยชน์ ฝากแชร์ต่อครับ 🙏

Leave a Reply

อีเมลของคุณจะไม่แสดงให้คนอื่นเห็น ช่องข้อมูลจำเป็นถูกทำเครื่องหมาย *