Building with Yoga
นี่คือจุดที่ทุกอย่างมาบรรจบกัน คุณได้ออกแบบ schema เขียน resolver เพิ่ม mutation จัดการ pagination กับ error ทำให้กราฟปลอดภัย และได้เรียนวิธีใส่ type เขียน test และ federate มาแล้ว บทนี้จะประกอบทุกแนวคิดเข้าเป็น TypeScript service เล็ก ๆ ตัวเดียวด้วย GraphQL Yoga — GraphQL server แบบ batteries-included ที่รันได้ทุกที่ที่ Node รันได้ — แล้วส่งโปรเจกต์ที่รันได้จริงให้คุณเอาไปต่อยอด
สามชิ้นส่วนของ Yoga service
หัวข้อที่มีชื่อว่า “สามชิ้นส่วนของ Yoga service”Yoga server สร้างจากสามชิ้นส่วนแบบเดียวกับที่ GraphQL server ทุกตัวมี เพียงแต่ตอนนี้ผูกเข้าด้วยกันอย่างชัดเจน:
- typeDefs — schema ในรูป SDL นี่คือ contract ของคุณ เขียนแบบ schema-first (ดูบท Schema-First vs Code-First) ข้างในมี
Queryสำหรับอ่านและMutationสำหรับเขียน - resolvers — function ที่ทำให้แต่ละ field มีค่าจริง คุณเจอมาแล้วใน Queries & Resolvers และต่อยอดในโมดูล mutations
- context — object ประจำ request ที่ Yoga สร้างครั้งเดียวแล้วส่งเข้าไปในทุก resolver เป็น argument ตัวที่สาม ใช้เก็บของที่มีขอบเขตแค่ request นั้น เช่น ผู้ใช้ปัจจุบัน handle ของ database และ request id เป็นที่วางของที่ Security & Performance สนใจได้อย่างสะอาด
Yoga นำสามชิ้นนั้นมาแปลงเป็น HTTP server ที่มี GraphiQL playground ในตัว, การจัดการ error ที่สมเหตุสมผล และรองรับ subscriptions — โดยคุณไม่ต้องเขียน boilerplate เองเลย
flowchart TD TypeDefs["typeDefs (SDL contract)"] Resolvers["resolvers (fulfil each field)"] Schema["Executable schema (createSchema)"] Context["createContext (per request)"] Yoga["createYoga"] Http["HTTP server + GraphiQL"] TypeDefs --> Schema Resolvers --> Schema Schema --> Yoga Context --> Yoga Yoga --> Http
context ร้อยทะลุไปอย่างไร
หัวข้อที่มีชื่อว่า “context ร้อยทะลุไปอย่างไร”context คือชิ้นส่วนที่มือใหม่ใช้ไม่เต็มที่ คุณนิยามฟังก์ชัน createContext ที่รันต่อหนึ่ง request — อ่าน header, ค้นหาว่าใครเป็นผู้เรียก — แล้วคืน object ออกมา Yoga ส่ง object นั้นให้ทุก resolver เป็น argument ตัวที่สาม ดังนั้น resolver ใด ๆ ก็อ่านได้ว่าใครเป็นคนถามโดยไม่ต้อง parse request ซ้ำ:
function createContext() { // Runs once per request; the return value is the resolver context. return { greetingPrefix: 'Hello' };}
const resolvers = { Query: { // The third argument is the context object above. hello: (_parent: unknown, args: { name: string }, ctx: { greetingPrefix: string }) => `${ctx.greetingPrefix}, ${args.name}!`, },};แบบนี้ resolver จะไม่ต้องยุ่งกับงานท่อประจำ request เลย แค่ขอสิ่งที่ต้องการจาก ctx ส่วนการ wire ก็อยู่ที่เดียว
Yoga server ที่รันได้จริง — query, mutation และ context
หัวข้อที่มีชื่อว่า “Yoga server ที่รันได้จริง — query, mutation และ context”โปรเจกต์ด้านล่างคือ schema.ts ฉบับเต็ม export ทั้ง typeDefs (schema เล็ก ๆ ที่มีหนึ่ง query และหนึ่ง mutation) และ resolvers (พร้อม store ในหน่วยความจำและคำทักทายที่อ่าน context) กด Open in StackBlitz เพื่อเปิด GraphQL Yoga server จริงใน browser แล้วเปิด GraphiQL playground ตาม URL ที่พิมพ์ออกมาเพื่อลอง operation ต่าง ๆ
Needs the Node.js runtime — open in StackBlitz to run.
พอ server รันแล้ว ลองอ่านข้อมูลก่อน:
{ greeting(name: "Graph") messages { id text } }greeting พิสูจน์ว่า context ไหลเข้ามา — prefix "Hello" มาจาก createContext ไม่ใช่จาก resolver จากนั้นรันการเขียนแล้วอ่านอีกครั้ง:
mutation { postMessage(text: "Built it end to end") { id text } }Query messages อีกครั้งแล้ว message ใหม่ของคุณก็จะอยู่ตรงนั้น ในไม่กี่บรรทัด คุณได้ออกกำลังให้ query หนึ่งอัน, mutation หนึ่งอัน, request context และ store ในหน่วยความจำ — รูปร่างแบบเดียวกับที่ Yoga service บน production ทุกตัวมี เพียงแต่เล็กกว่า
บทนี้อยู่ตรงไหนของคอร์ส
หัวข้อที่มีชื่อว่า “บทนี้อยู่ตรงไหนของคอร์ส”service ตัวนี้คือคอร์สทั้งคอร์สย่อส่วน typeDefs คือการออกแบบ schema, resolvers คืองานจากโมดูล queries-and-resolvers กับ mutations, context คือจุดที่ security และ performance เข้ามาเกี่ยว เติม codegen กับ test จากบทก่อนหน้าในโมดูลนี้ แล้วคุณจะได้ service ที่มี type และผ่านการ test จากนั้นแยกตาม domain แล้ววาง gateway ไว้ข้างหน้า ก็ได้บท federation ครบ ไฟล์เล็ก ๆ ไฟล์เดียว แต่มีทุกแนวคิด
| ข้อดี | ข้อแลกเปลี่ยน |
|---|---|
| envelop plugin system — composable middleware สำหรับทุก phase | plugin order สำคัญ — ผิดลำดับทำให้ behavior ผิด |
| รองรับ Node.js, Deno, Bun, Cloudflare Workers | ecosystem เล็กกว่า Apollo Server |
| built-in GraphiQL IDE — development ง่าย | ต้องเรียนรู้ envelop ถ้าต้องการ custom plugin |
| lightweight กว่า Apollo Server — startup เร็วกว่า | community resource น้อยกว่า Apollo |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”ใช้ Yoga โดยไม่มี Envelop Plugin สำหรับ Production อาการ:
- deploy production โดยไม่มี auth plugin, rate limit plugin, logging plugin
- default Yoga setup เหมาะกับ development ไม่ใช่ production
- เพิ่ม plugin ที่จำเป็นก่อน production:
useAuth,useRateLimit,useOpenTelemetry
GraphiQL เปิดใน Production อาการ:
- GraphiQL accessible ใน production — introspection ให้ข้อมูล schema ทั้งหมด
- ปิด GraphiQL และ introspection ใน production หรือ protect ด้วย auth
💡 ตัวอย่างจากของจริง
The Guild (Yoga creators):
- ใช้ Yoga ใน production สำหรับ client ขนาดใหญ่หลายราย
- plugin ecosystem รวม DataLoader, Rate Limit, Auth, Tracing
Hive (GraphQL Schema Registry):
- สร้างโดย The Guild — integrate กับ Yoga โดยตรง
- track schema usage, detect breaking change, monitor performance