
สารบัญ
- WebFlux + GraphQL คืออะไร
- ทำไมต้องจับคู่สองเทคโนโลยีนี้
- สแตกเทคโนโลยีและ dependency
- ออกแบบ Schema GraphQL
- เขียน Resolver แบบ Reactive
- Router Functions แบบ Non-Blocking
- GraphQL Subscription ผ่าน WebSocket
- Security & Validation
- Unit Test และ Integration Test
- Logging & Tracing ที่เหมาะสม
- Deploy บน Kubernetes
- Best Practices
- สรุป
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(); }

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
เฉพาะ profiledev
- ใส่ 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
- ใช้ DataLoader รวม batch SQL ภายใน resolver
- แยก DTO ออกจาก entity เพื่อลด field leakage
- ตั้ง Timeout + CircuitBreaker รอบ call ไป service อื่น
- ใช้ 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 • หากบทความนี้มีประโยชน์ ฝากแชร์ต่อครับ 🙏