
สารบัญ
- 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 • หากบทความนี้มีประโยชน์ ฝากแชร์ต่อครับ 🙏