สร้าง GraphQL API แบบง่ายๆด้วย Elixir (จริงๆนะ)

Pongsatorn Paewsoongnern
Norze Code
Published in
3 min readAug 7, 2018

(´。• ᵕ •。`) ผมเชื่อว่าหลายๆคนคงคุ้นเคยกับการสร้าง REST API กันมาแล้วแน่นอน บางคนอาจจะเบื่อและอยากลองทำอะไรใหม่ๆ วันนี้ผมเลยจะขอพามาเปลี่ยนบรรยากาศด้วยการทำ API Service ด้วยน GraphQL กัน โดยใช้ภาษา Elixir ที่คุ้นเคย (ขายของ) รับรองว่าง่ายและน่าสนใจแน่นอน! (─‿─)

GraphQL คืออะไร

A query language for your API มันจะทำให้เราสามารถควบคุม Data ทั้งหมดใน API ของเราได้อย่างอยู่หมัด ทั้ง Description, Versioning และการ Deprecated สิ่งที่ไม่ได้ใช้แล้ว

  • เบื่อไหม? ที่จะต้องมานั่งทำ API Document บ่อยๆ อัพเดททีก็ต้องมาไล่แก้ Doc ที
  • เบื่อไหม? กับการต้องมานั่งกังวลเวลาจะเพิ่มหรือลดข้อมูลใน API เพราะกลัว​กระทบกับ Client ที่มาเรียกใช้อยู่ แถมจะทำ Versioning ก็ต้องคิดท่านู่นนั่นนี่เยอะไปหมด
  • เบื่อไหม? เวลาเรียกข้อมูลจาก API มาแล้วมีอะไรไม่รู้เยอะแยะเต็มไปหมด ทั้งๆที่จะใช้แค่ 2–3 Fields เอง จะ Debug ทีนี่มึนเลย~

ถ้าคุณกำลังประสบกับปัญหาเหล่านี้ เราขอเสนอทางออกให้ . . . . มาใช้ GraphQL กันเถอะ!

ก่อนจะเข้าไปถึงในส่วน Implementation จะขอเปรียบเทียบ Scenario เล็กๆให้พอเข้าใจคร่าวๆก่อนว่า วิธีการเรียกใช้ GraphQL API มันต่างจาก REST API ยังไง

Scenarion: สมมติว่าเราต้องการข้อมูลของฮีโร่ตัวนึง พร้อมกับรายชื่อเพื่อนๆของเรา มาดูกันว่าระหว่าง REST และ GraphQL จะเรียกต่างกันยังไง

REST API

Endpoint: https://api.domain.com/heroes/20
*เรียกด้วยการส่ง HTTP GET ไปตามปกติ

และนี่ก็คือสิ่งที่เราได้กลับมาจาก API ซึ่งก็น่าจะดูออกกันอยู่แล้วไม่ต้องอธิบายมาก เดี๋ยวเราลองไปดูกันต่อเลยดีกว่า ว่าถ้าเป็น GraphQL มันจะออกมาเป็นหน้าตายังไงกันนะ~

GraphQL API

Endpoint: https://api.domain.com/api
*ต้องเรียกเป็น HTTP POST เท่านั้น พร้อมกับ Request Body ประมาณนี้ ...

อ๊ะ! เห็นไหมว่าในการส่ง Request สำหรับ GraphQL เราจะต้องเขียน Query เพิ่มเข้าไปพร้อมกับ Body ด้วย ทำไมละ?! เนื่องจาก Endpoint ของเรามีเพียง URL เดียว และมันก็ไม่ได้บ่งบอกถึง Action ที่เราต้องการจะทำนั่นเอง โดยระบุว่า ต้องการข้อมูลอะไร และต้องการให้ส่งอะไรกลับมาบ้าง~ ดังนั้นจะบอกว่าเราสามารถควบคุม Data ที่ต้องการได้อย่างอยู่หมัดเลยทีเดียว ฮั่นแน่!เริ่มเห็นแล้วใช่ไหมว่ามัน Flexible ขนาดไหน

และแล้วเราก็ได้ข้อมูลกลับมาแล้ว ดูเผินๆก็แทบจะไม่ต่างจากข้อมูลที่ได้จากการเรียก REST API ปกติเลย เพียงแค่ structure level ของข้อมูลต่างกันนิดหน่อย แต่จุดที่สำคัญคือตัว data ที่ส่งกลับมาจะมีเฉพาะ fields ที่เราส่งไปพร้อม query เท่านั้น เช่นถ้าใน query ผมระบุไปแค่ field name และ class ก็จะมีแค่ข้อมูล name และ class ของฮีโร่ส่งกลับมานั่นเอง เป็นไงบ้างครับ น่าสนใจเพิ่มขึ้นรึยัง?

แล้วเขียน GraphQL API ด้วย Elixir ยากไหม?

ไม่ยากเลยครับ มันง่ายมาก :) ถึงแม้ว่าตัว GraphQL เองทาง Facebook พัฒนาขึ้นมา Based on JavaScript ตั้งแต่แรก แต่ด้วยพลังของ Elixir ที่เป็นภาษา Dynamic Type และ Functional Programming ทำให้มันเข้ากับ GraphQL ได้อย่างลงตัวเลยทีเดียว

สิ่งที่ต้องการในการ Implement (ลืมบอก)

  • Elixir -> อ่านวิธีลงได้จาก Installation Guide
  • Phoenix Framework -> อ่านวิธีลงได้จาก Installation Guide
  • Atom / VSCode -> ใช้ IDE ตัวไหนก็ได้แล้วลง Elixir Plugin ให้เรียบร้อย
  • ถ้าเกิดใช้ Atom (ผมใช้ lol) แนะนำให้ลง Plugin Atom IDE UI ตามด้วย IDE Elixir

สำหรับคนที่ใช้ macOS สามารถลง Elixir ได้ง่ายเพียงแค่

brew update && brew install elixir

เพื่อทำการ Install Elixir และ Erlang Runtime และต่อด้วยคำสั่ง

mix archive.install https://github.com/phoenixframework/archives/raw/master/phx_new.ez

เพื่อลง Phoenix Framework~ จากนั้นเราก็พร้อมที่จะไปลุยของจริงกันแล้ว!

ปล. เราจะใช้ Library ของ Elixir เข้ามาช่วยในการ Implement ครั้งนี้ นั่นคือ

  • Absinthe -> เป็น GraphQL Toolkit สำหรับ Elixir (ไม่ต้องลงเอง)

เอาหล่ะ มาเริ่มกันเลยทุกคน :D

mix phx.new graph --no-ecto --no-brunch --no-html

สร้างโปรเจค Phoenix Framework ชื่อว่า graph โดยมี attributes ดังนี้

  • — no-ecto : บอกว่าไม่ต้องใส่อะไรที่เกี่ยวกับ Ecto มานะ (Database ORM)
  • — no-brunch : บอกว่าไม่ต้องใส่ Brunch มานะ (Frontend Package Management)
  • — no-html : บอกว่าไม่ต้องใส่สิ่งที่เกี่ยวกับ HTML มานะ เพราะเราจะเขียนแค่ API

ทำการแก้ไขไฟล์ mix.exs เพื่อเพิ่ม Library absinthe และ absinthe_plug เข้าไปยังโปรเจคของเรา

*อย่าลืมพิมคำสั่ง mix deps.get เพื่อให้ตัว Build Tool ดาวน์โหลด Library ที่เราเพิ่มเข้าไปด้วย

เรามาเริ่มจากการสร้าง GraphQL Schema Type ขึ้นมากันก่อนเนอะ~

  • สร้างไฟล์ /lib/graph_web/schemas/hero_types.ex
  • เขียนโค๊ดตามตัวอย่างด้านบน ได้เลย :3
  • เป็นการสร้าง Object ขึ้นมา และกำหนดว่าภายในนั้นจะมี Field และ Type อะไรบ้าง
  • สามารถเขียน Resolve Function ให้ฟัง Field นั้นได้อีกด้วยถ้าหากต้องการ (ตัวอย่าง Resolver มีด้านล่าง)

ปล. Object นี้ใช้สำหรับ GraphQL Only นะฮัฟฟฟ

จากนั้นก็สร้าง GraphQL Schema เพื่อกำหนด Query ที่จะใช้ใน API ของเรา

  • สร้างไฟล์ /lib/graph_web/schema.ex
  • เขียนโค๊ดตามตัวอย่างด้านบนได้เลยเช่นกัน :3
  • เรา Import Type ที่เราเขียนไว้ Step ด้านบนมาใช้
  • เรา Defined Query ไว้ 2 อย่าง อย่างแรกคือ :heroes โดยจะได้ ลิสต์ของ Object :hero กลับไป และ อย่างที่สองคือ :hero โดยจะได้รับ Object :hero กลับไป
  • โดย Query :hero จะมีจุดแตกต่างคือรับ Argument มา 1 ตัวชื่อว่า :id โดยมี Type เป็น :id (ใน Elixir คือ Sequencial Integer)
  • โดยทั้งสองจะเรียก Resolver Function แตกต่างกันไปตามจุดประสงค์ของ Query
  • ไม่ต้อง งง ว่า /3 คืออะไร มันคือ Arity ของ Function เพื่อบ่งบอกว่าเรียกใช้ฟังชั่นชื่อนี้ ที่รับ 3 Arguments นะ

เกือบจะท้ายสุดแล้ว เราก็ต้องมาเขียน Resolver Function เพื่อทำหน้าที่ไปตามหาข้อมูลมาให้ตามที่ Schema ต้องการ

หลักๆของ Resolver คือการไปหา Data มาให้ Schema โดยถ้าเกิด Success ให้ตอบกลับไปว่า {:ok, data} หรือถ้ามีอะไรผิดพลาดก็ตอบกลับไปว่า {:error, reason}

โดยเราสามารถเขียนเองได้ตามใจชอบเลยว่าจะไป Query ข้อมูลจากไหนมาให้ อาจจะไปเอามาจาก Database หรือแม้กระทั่งไปเรียกเอาจาก Service อื่นๆที่เราเขียนไว้อีกก็ได้เช่นกัน

  • แต่ในตัวอย่างด้านบนผมขอสร้าง Data มั่วๆขึ้นมาเพื่อส่งกลับไปให้ Schema เลย จะได้ไม่ต้องเสียเวลาเนอะ

เอาแล้ววว! มาถึงขั้นตอนสุดท้ายแล้วครัช! Implement ตั้งนาน แต่ถ้าเราไม่ได้สร้าง Route ให้มันก็จบเห่ Client ยิง Request มาไม่ได้ 5555+

  • แก้ไขไฟล์ /lib/graph_web/router.ex
  • ตามภาพด้านบนเลย :3 ก๊อปได้เลยฮะ
  • forward “/graphiql” คือเป็นการเปิดใช้ GraphiQL ซึ่งเป็น Client ให้ใช้บนเว็ป จะได้ลองเล่นกันได้สะดวกขึ้น (ถ้าบน Production จะปิดไว้)
  • forward “/” อันล่างสุดคือกำหนด Endpoint เพื่อให้ Client เรียกใช้เข้ามายังจุดนี้~ ในภาพก็คือจะเป็น /api นั่นเอง

เพียงเท่านี้ก็เรียบร้อยแล้ว ลองรันเว็ปเซิฟเวอร์ด้วยคำสั่ง mix phx.server แล้วเปิด GraphiQL Client เพื่อลองเล่นสิ่งที่เราทำมากันได้เลยครับ

สามารถเข้า GraphiQL Client ได้ที่ลิงก์ http://localhost:4000/api/graphiql แล้วก็จะเจอกับหน้าตาดังภาพ สามารถลองพิมพ์ Query เล่นได้เต็มที่

  • ด้านขวามือจะมี Document อยู่ อ้างอิงจาก Schema ของเราว่าสามารถ Query ข้อมูลอะไรยังไงได้บ้าง มีให้ครบหมดที่ต้องการ สุดยอดไปเลย!

บทสรุป

มาถึงตรงนี้ คิดว่าเพื่อนๆคนจะได้ลองเล่น GraphQL กันไปไม่มากก็น้อยแล้วใช่ไหมครับ สนุกใช่ไหมหล่ะ ส่วนตัวผมคิดว่ามัน Flexible และ Powerful มาก เหมาะแก่การเรียนรู้ไว้ จะใช้รึปล่าวเป็นอีกเรื่องนึง แฮร่~

อ้อ! อย่าพึ่งคิดว่า REST ไม่มีประโยชน์นะครับ จริงๆแล้วมันยังมีอยู่ เช่นความเร็วในการ Implement ที่เร็วกว่า เพราะในช่วงแรกเราคงไม่เอา GraphQL ไป Implement ทุก Service หรอกจริงไหม? (ไม่รู้ เดาเอา อิอิ) เราอาจจะหยิบเอามาใช้แค่ในส่วนของ Data Gateway ที่ทำหน้าที่ Data Aggregation รวมข้อมูลจากหลายๆแหล่งส่งให้กับ Client จะได้ลด Overhead ของ Request ลง ส่วน Service เล็กๆเราอาจจะ Implement เป็น REST ปกติก็ได้ไม่เสียหาย :)

ทั้งนี้ก็ขึ้นอยู่กับตัว Developer เองว่าจะเลือกทางไหน เพราะ Developer Happiness คือสิ่งที่สำคัญที่สุด ถ้ามันทำให้ทุกคนแฮปปี้ทำงานร่วมกันได้ดีขึ้น ผลงานที่ออกมาก็จะยอดเยี่ยมแน่นอน

แล้วพบกันใหม่ครับ — SukinoSenze

--

--