ข้ามไปยังเนื้อหา

Fragment และ Alias

บทเรียนสุดท้ายของโมดูลนี้ว่าด้วยเครื่องมือสามอย่างที่ทำให้ query สะอาดขึ้น ไม่ใช่ทรงพลังขึ้น Fragment ให้ตั้งชื่อ selection set แล้วนำกลับมาใช้ซ้ำ Inline fragment ให้ selection แตกแขนงตาม type ส่วน Alias ให้เปลี่ยนชื่อ field ใน response ทั้งสามอย่างไม่เปลี่ยนสิ่งที่ server ทำเลย เปลี่ยนแค่ว่า query ของคุณอ่านง่ายและยืดหยุ่นแค่ไหน

พอ field ชุดเดียวกันโผล่หลายที่ การก็อปไปวางทุกจุดคือกับดักตอน maintain ตัว fragment คือ selection set ที่มีชื่อ นิยามครั้งเดียวแล้ว spread ไปใช้ตรงไหนก็ได้ ประกาศด้วย keyword fragment และระบุ type ที่ใช้ได้ด้วย on

fragment TrackFields on Track {
id
title
durationSeconds
}

จากนั้น spread เข้าไปใน selection set ด้วย operator ... ผลลัพธ์เหมือนเขียน field ชุดนั้นไว้ตรงนั้นเลย

{
track(id: "t1") {
...TrackFields
}
featured: track(id: "t2") {
...TrackFields
}
}

selection ทั้งสองชุดดึง id, title และ durationSeconds เข้ามาโดยไม่ต้องเขียนซ้ำ แก้ fragment ที่เดียว ทุกจุดที่ spread ก็อัปเดตตามหมด ได้ผลตอบแทนแบบเดียวกับการแยก code ที่ซ้ำกันออกมาเป็น function

บางที field ก็คืน type ที่เป็นได้หลาย object type คือ interface หรือ union นั่นเอง เวลาจะเลือก field ที่มีอยู่เฉพาะบน type ใด type หนึ่ง ให้ใช้ inline fragment เขียนเป็น operator ... ตามด้วย on SomeType แล้วต่อด้วย selection set โดยไม่ต้องตั้งชื่อ

{
searchResult {
... on Track {
title
}
... on Artist {
name
}
}
}

กรณีนี้ searchResult จะ resolve เป็น Track หรือ Artist ก็ได้ ตัว inline fragment จึงบอกว่า “ถ้าเป็น Track ขอ title ถ้าเป็น Artist ขอ name” แล้วมีเฉพาะแขนงที่ตรง type เท่านั้นที่โผล่ใน response นี่คือวิธีแตกแขนงตาม type ภายใน selection เดียว

flowchart TD
  Frag["fragment TrackFields on Track { id, title, durationSeconds }"]
  Frag -->|"... spread"| S1["hit: track(id: t1)"]
  Frag -->|"... spread"| S2["deepCut: track(id: t2)"]
  S1 --> R["Response with two aliased keys"]
  S2 --> R
fragment ถูกนิยามครั้งเดียวและถูก spread เข้าไปในหลาย selection

ปกติ field จะโผล่ใน response ด้วยชื่อของตัวเอง ส่วน alias ให้คุณเลือก key อื่นได้ เขียนเป็น aliasName: fieldName นำหน้า field ตัวนี้จำเป็นตอนที่เลือก field เดียวกันสองครั้งด้วย argument คนละชุด เพราะถ้าไม่มี alias ผลลัพธ์สองอันจะชนกันที่ key เดียวกัน

{
first: track(id: "t1") {
title
}
second: track(id: "t2") {
title
}
}

response จะมีสอง key แยกกันคือ first และ second แต่ละตัวเก็บ track ของตัวเอง alias เป็นเรื่องของ รูปร่าง response ล้วน ๆ ไม่แตะ schema หรือ resolver เลยสักนิด และยังสะดวกเวลาต้องดัดชื่อ field ให้ตรงกับที่ client code รออยู่

ตัวอย่างด้านล่างนิยาม fragment ชื่อ TrackFields แล้ว spread เข้าไปใน selection ของ field track สองชุด พร้อมใช้ alias hit กับ deepCut ให้ผลลัพธ์แยกคนละ key กด Run แล้วดู key ของ response

JavaScript

response มี key ระดับบนสุดสองตัวคือ hit และ deepCut แทนที่จะเป็น track ตัวเดียวที่ชนกัน นั่นคือผลงานของ alias และแต่ละตัวก็เก็บ field ครบชุดจาก TrackFields ที่เขียนครั้งเดียวแล้วใช้ซ้ำสองรอบ นั่นคือผลงานของ fragment สองอย่างรวมกันทำให้ query สั้นลงและ response มีรูปร่างที่ใช้งานง่าย

ข้อดีข้อแลกเปลี่ยน
Fragment ลด duplication ของ field selection ข้าม queryfragment มากเกินทำให้ query อ่านยาก
Alias ทำให้ query field เดียวกันสองครั้งด้วยชื่อต่างกันFragment ที่ไม่ใช้สร้าง dead code ใน client
Fragment ช่วยให้ component-driven query (Relay)Alias ที่ไม่สื่อความหมายทำให้ response งงยิ่งขึ้น
Inline fragment ช่วย handle Union และ Interface typeFragment spread ลึกมากทำให้ query ขนาดใหญ่

Copy-paste Field แทนใช้ Fragment อาการ:

  • query 5 ชิ้นมี field ชุดเดียวกัน (id, name, avatar) เขียนซ้ำทุกที่
  • เปลี่ยน field เดียวต้องแก้ 5 query
  • สร้าง fragment UserBasicInfo แล้ว spread ใน query ทั้งหมด

Alias ที่ไม่สื่อความหมาย อาการ:

  • data1: user(id: 1) { name } และ data2: user(id: 2) { name }
  • response มี data1 และ data2 ที่ client ต้องรู้ว่าหมายถึงอะไร
  • ใช้ alias ที่สื่อ intent: primaryUser: user(id: $id1), secondaryUser: user(id: $id2)

💡 ตัวอย่างจากของจริง

Relay (Meta/Facebook):

  • บังคับ pattern “colocation” — component ประกาศ fragment ของตัวเองที่ต้องการ
  • query ระดับ page รวม fragment จาก component ทั้งหมดอัตโนมัติ

Apollo Client:

  • useFragment hook ทำให้ component subscribe เฉพาะ field ที่ตัวเองประกาศใน fragment
fragment แก้ปัญหาอะไร?
inline fragment อย่าง "... on Track" จำเป็นตอนไหน?
ทำไมต้องใช้ alias เมื่อเลือก field เดียวกันสองครั้งด้วย argument คนละชุด?