🎞️ Videos Redis cache decorator for hexagonal architecture in NestJS

Description

We designed our system with a hexagonal architecture in NestJS. Our database is MongoDB. But we don't want to hit MongoDB with every request. We want to use cached data from Redis without changing a lot of our hexagonal code. Finally, we chose a decorator to solve this problem. We have 2 decorators. 1. Cache decorator 2. Invalid cache decorator Slides https://docs.google.com/presentation/d/e/2PACX-1vRdpkTV9l-nflcVs6xmur8pN6BUTwRzl8XN9vZnkj1x_fnZnOs1J34XvYkJdLcMPdPYd-X4fbq0HFqR/pub?start=false&loop=false&delayms=3000&slide=id.p Git Repository https://github.com/Eji4h/redis-cache-decorator-for-hexagonal-architecture-in-nestjs

Chapters

  • แนะนำตัวและเกริ่นนำ Hexagonal Architecture กับ NestJS 0:00
  • Decorator คืออะไร? เปรียบเทียบกับ Annotation และ Attribute 1:41
  • Decorator ใน TypeScript และ NestJS 2:42
  • ตัวอย่าง Decorator ใน NestJS: Controller, Get, Module, Injectable 3:05
  • Hexagonal Architecture คืออะไร? (Ports and Adapters) 4:13
  • Driving Side และ Driven Side - การเชื่อมต่อแบบ Port 5:18
  • ข้อดีของ Hexagonal Architecture: ลด Coupling, เทสต์ง่าย, Logic ไม่กระจาย 6:20
  • ตัวอย่างการ Implement Hexagonal Architecture ใน NestJS 7:27
  • Demo Code: Controller, Use Case, Repository, Interface 8:59
  • การ Map Object จาก Database กลับเป็น Domain Model และ Branded Type 11:29
  • Branded Type คืออะไร? เพิ่มความ Strict ให้กับ Type 12:57
  • ตัวอย่างการใช้ Branded Type ใน Domain Model 14:25
  • ข้อดีของ Hexagonal Architecture: เปลี่ยน Database ได้ง่าย 15:16
  • ปัญหา Caching ใน NestJS และ TTL 16:04
  • Solution แรก: แก้ที่ Use Case (ข้อดีข้อเสีย) 17:52
  • Solution ที่สอง: แก้ที่ MongoDB Repository (ข้อดีข้อเสีย) 19:40
  • Final Solution: ใช้ Decorator สำหรับ Caching 20:41
  • Decorator สำหรับ Method ของ Class ทำงานอย่างไร 21:47
  • ตัวอย่าง Decorator Memorize 23:10
  • Caching Decorator ทำงานอย่างไร 24:06
  • Invalidating Cache Decorator ทำงานอย่างไร 24:38
  • ตัวอย่าง Key Combination สำหรับ Caching 25:01
  • Demo Code: Repository, Base Key, Key Combination 25:37
  • Demo Code: Caching Decorator Implementation 26:56
  • Demo Code: Setting and Getting Cache 28:55
  • Demo Code: Unit Test สำหรับ Decorator 29:02
  • Demo: ยิง Request ให้ดูการทำงานจริง 30:27
  • สรุปและข้อจำกัดของ Caching Decorator 34:34
  • ปัญหา Key ที่ใช้ในการ Search เปลี่ยนแปลงได้ 36:15
  • ผลลัพธ์หลังจาก Implement Caching Decorator: Load ลดลง, ราคาลดลง 36:41
  • ช่วงขายของ: Course RESTful API by NestJS 36:54
  • Q&A: REST Client Plugin ใน VS Code 37:18

Transcript

คำบรรยายต่อไปนี้อาจไม่ถูกต้องทั้งหมด หากคุณพบข้อผิดพลาดใดๆ คุณสามารถช่วยแก้ไขข้อผิดพลาดได้บน GitHub

แนะนำตัวและเกริ่นนำ Hexagonal Architecture กับ NestJS0:00

โอเคครับ

ผมไม่แน่ใจนะว่าทุกคนรู้จักพวก hexagonal กันขนาดไหนเนาะ แต่ว่าผมว่าน่าจะเคยได้ยินพวก NestJS กันมาบ้างนะครับ โอเค อันนี้เดี๋ยวผมแนะนำตัวก่อนนะครับ ก็ชื่อเล่นชื่อเป้นะครับ ก็มีบล็อกแล้วก็มีเพจ jitrak.dev นะครับ

หลายคนอาจจะสงสัยว่า เฮ้ย จริงๆ แล้วอะ ไอ้คนนี้มันไปไหนวะ ทำไมวันนี้มันไม่มาเนาะ เออ มันเป็นอะไรของมันทำไมมันไม่มานะครับ โอเคครับ ก็เพิ่งเป็นโควิดครับ แบบสดๆ ร้อนๆ เลยนะครับ ก็เลยมาไม่ได้นะครับ โดนแบนออกจากงาน

แล้วเค้าก็ส่งวิดีโอมา ไม่แน่ใจว่าจะได้ยินหรือเปล่านะครับ สวัสดีครับ ผมแบนนะครับ ขอบคุณทุกคนมากนะครับที่ให้ความสนใจเข้ามาฟังใน session นี้ นะครับ ก่อนผมต้องบอกเลยว่าตัวผมเองเนี่ยไม่สามารถเข้าไป ร่วมงานหรือไปเจอทุกคนได้โดยตรงนะครับ เนื่องจากว่าตัวผมเนี่ยเพิ่งฟื้นจากโควิดมานะครับ แต่ว่าทุกคนไม่ต้องเป็นห่วงไปนะครับ เพราะว่าเป้กับผมเนี่ยเราได้เตรียมเนื้อหามาร่วมกันแล้วว่า เรา implement อะไรไปแล้วจะเอามาแชร์ให้ทุกคนนะครับ ยังไงต้องบอกเลยว่าหวังว่าทุกคนนะครับจะสนุกและหรือ ได้แนวคิดอะไรสักอย่างเอาไปประยุกต์ใช้กับงานของตัวเอง นะครับ ก็ต้องขอโทษอีกครั้งที่ไม่สามารถไปด้วยตัวเองได้ แล้วก็ขอบคุณทุกคนมากนะครับที่เข้ามาฟัง session ของเรา ขอให้สนุกนะครับ

ครับ ก็โดนแบนนะครับ แต่ว่าส่งอัดวิดีโอมาเมื่อเช้าให้นะครับ

สไลด์หน้าถัดไป

Decorator คืออะไร? เปรียบเทียบกับ Annotation และ Attribute1:41

โอเคครับ ก็เราพูดถึง decorator เนาะ ผมก็อยากจะเล่าก่อนเนาะว่า decorator เนี่ยมันคืออะไรเนาะ ถ้าแบบมาถึงเรากระโดดข้ามไปดูโค้ดจริงเลยเนี่ย มันอาจจะแบบงงกันได้เนาะว่าเกิดอะไรขึ้นนะครับ โอเค จริงๆ แล้วอะ ในภาษาอื่นๆ นะครับ ใน framework อื่นๆ น่ะ อย่าง Spring Boot หรือ .NET เนี่ยเค้าก็มีของที่คล้ายๆ กันอยู่เนาะ ไม่แน่ใจว่าใครเคยแตะ Spring Boot หรือฝั่ง .NET มาบ้างนะครับ จริงๆ แล้วอะ ถ้าใน Java นะครับ เค้าก็จะเรียกว่าใน Spring Boot อะเนาะ ก็จะเรียกว่า annotation นะครับ เพราะถ้าของที่มาจาก Google เนี่ย ไอ้พวกเนี้ยจะเรียกว่า annotation กันนะครับ อย่างเช่นพวก @Controller หรืออะไรอย่างนี้นะครับ อันนี้ผมยกตัวอย่างให้ง่าย ครับ หรือว่าถ้าฝั่ง C# ก็จะเรียกว่า attribute นะครับ มันก็จะเห็นว่ามันเป็นตัววงเล็บนะครับ สี่เหลี่ยมนะครับ

Decorator ใน TypeScript และ NestJS2:42

คราวนี้ ใน TypeScript นะครับ มันก็คล้ายๆ กันนะ แต่ว่ามันเรียกว่า decorator แทนนะครับ

หลักๆ ก็คือแบบเหมือนเป็นแบบทำเรื่อง add annotation

แล้วก็ meta programming ให้กับ class หรือ member ของ class ได้นะครับ

ตัวอย่าง Decorator ใน NestJS: Controller, Get, Module, Injectable3:05

คราวนี้เราพูดถึง NestJS เนาะ ผมก็ให้ดูตัวอย่างของ NestJS กันนะครับ ก็จะเห็นว่ามันมี decorator ที่เป็น @Controller กับ @Get อยู่นะครับ ก็คืออันนี้ทำเหมือนทำ controller เพื่อรับ HTTP request โดยที่แบบแปะแค่ 2 อันนี้เข้าไปมันก็สามารถทำได้ง่ายๆ แล้วนะครับ ครับ ข้อดีของ NestJS อีกอย่างนึงนะครับ ก็คือมันมีเรื่องพวกตัว injectable หรืออะไรอย่างนี้ ให้เราได้ด้วยนะครับ แต่ว่าอันนี้ก็เป็นตัวอย่างง่ายๆ เนาะ ครับ แล้วก็เราก็เอาของเมื่อกี๊อะครับ มารวมอยู่ในนี้ทีเดียวเลยนะครับ ก็ใส่ @Module นะครับ แล้วก็เอา controller ที่สร้างเมื่อกี๊กับ app service ตรงเนี้ยมาแปะไว้ให้มันนะครับ เดี๋ยวตัว app controller เนี่ย ตัวเนี้ยนะครับ มันก็จะโดน inject ของเข้ามาให้เองนะครับ

จริงๆ แล้วมันก็มี decorator อีกเยอะมากเลยนะครับ ใน NestJS เนี่ยที่เราสามารถใช้งานได้นะครับ

Hexagonal Architecture คืออะไร? (Ports and Adapters)4:13

เมื่อกี๊เราพูดถึงปูพื้นเรื่อง decorator ไปแล้ว คราวนี้เรามาพูดถึงเรื่อง hexagonal บ้างนะครับ ก็จริงๆ แล้ว ก็มีสรุปนะ สั้นๆ นะครับ

ก็คือมีคนชื่อเนี้ยเนาะ แล้วก็ชื่อ hexagonal architecture อีกชื่อนึงน่ะ หรือเค้าก็เรียกว่า port หรือ port และ adapter architecture นะครับ อันนี้เราลองดูรูปนะครับ ก็จะมีฝั่งซ้ายและฝั่งขวานะครับ ฝั่งซ้ายนี่ก็จะเป็น driving side นะครับ วิ่งเข้าผ่าน application ไปนะครับ แล้วก็ไปออกฝั่ง driven side อีกทีนึง

จริงๆ แล้วมันจะเป็นอย่างนี้นะครับ ให้มองว่ามันจะเป็นตัว port port เชื่อม 2 ฝั่งนะครับ ฝั่งขาเข้าเนี่ยจะเห็นว่ามันเหมือนมันเสียบปลั๊กเนาะ เสียบปลั๊กนะครับ เอามาปลั๊กเข้ากับ port แล้วก็ไปวิ่งข้างใน application เสร็จแล้วอะมันก็ไปออกกับอีก port นึง โดยที่เหมือนเสียบปลั๊กเหมือนกัน เหมือนเราเสียบ USB ถ้าอธิบายให้เห็นภาพนะครับ

Driving Side และ Driven Side - การเชื่อมต่อแบบ Port5:18

ถ้าลองดูด้านซ้ายนะครับ จะเห็นว่าด้านซ้ายเนี่ยจะเป็น server เรียกมา หรือเป็น user คนที่เรียกเข้ามาก็ได้นะครับ เสร็จแล้วเนี่ยก็จะวิ่งเข้ามาใน application เนาะ ฝั่งขวาเนี่ยอาจจะออกเป็นไฟล์หรือไปเซฟลง หรือไปอ่านข้อมูลจาก database เอานะครับ

ด้านซ้ายเนี่ยให้มองว่ามันเป็นอาจจะเป็น user interface ก็ได้นะครับ เป็นขาเข้าเนาะ ส่วนฝั่งขวาเนี่ยก็ไปต่อกับพวก infrastructure ก็แบบเขียนลงเครื่อง เขียนไฟล์ลงเครื่องหรือต่อ database

นะครับ

อันนี้ก็ให้เห็นภาพนะครับว่าแบบเออเนี่ย ตัว application ตรงกลางเนี่ยจะเห็นว่ามันมี domain ย่อยเข้าไปอีกด้วยนะครับ แต่ว่าอันนี้ผมคงไม่ลงลึกเนาะ

จริงๆ แล้วเนี่ยมันก็คล้ายๆ พวก clean architecture หรืออะไรพวกนี้เหมือนกันนะครับ

ข้อดีของ Hexagonal Architecture: ลด Coupling, เทสต์ง่าย, Logic ไม่กระจาย6:20

ก็หลักๆ แล้วเนี่ยการทำแบบเนี้ยมัน มันดียังไงเนาะ ก็อย่างแรกเลยนะครับ ก็ลดความผูกติดกับ framework นะครับ ทำให้โค้ดเราเนี่ยเทสต์ได้ง่ายนะครับ เทสต์ง่ายมากๆ จริงๆ เลยนะครับ ถ้าแบบเขียน unit test อะไรอย่างเงี้ย ถ้าแบบเราไม่ดีไซน์เป็น hexagonal เนี่ย เวลาเราจะเขียนเทสต์เนี่ย เรามี hit database หรืออะไรอย่างเงี้ย ปวดหัวนะครับ มาม็อก database กันนะครับ

ไม่ depend กับ UI ด้วยเนาะ แล้วก็แบบสมมติว่าเราไปต่อ API ข้างนอก มันไปยิงข้างนอกจริงๆ อะเนาะ มันก็เปราะบางนะครับ

ตัว objective ของเรื่องพวกเนี้ยเนี่ยก็คือ ทำให้มันแบบไม่ depend กับ framework แล้วก็สามารถเขียนเทสต์ได้ง่ายนะครับ ลอจิกมันไม่กระจายด้วยนะ ต้องพูดอย่างนี้นะครับ

อันนี้ก็เป็นรูปแบบเต็มเนาะ ซึ่งผมคงไม่พาไปทัวร์เพราะว่าภาพดับอีกแล้ว

โอเคครับ ก็อันนี้ไปอ่านได้นะครับ เดี๋ยวมีแจกสไลด์ตอนท้ายนะครับ

ตัวอย่างการ Implement Hexagonal Architecture ใน NestJS7:27

อันนี้ลองมาดูกันนะครับแบบอธิบายเป็นรูปเนาะ ให้เห็นภาพกันนะครับ ด้านซ้ายเนี่ยเป็นขาเข้าเนาะ เป็น adapter นะครับ อาจจะมาเรียก use case ของเรานะครับ แล้วก็ไปต่อกับฝั่งที่เป็น port

เมื่อกี้ไม่เห็นภาพ เราแปลงให้มันเป็นของจริงหน่อยนะครับ ก็จะเห็นว่าเราเป็นตัว controller ที่รับ HTTP request นะครับ

ตัวเนี้ยมันก็จะไปเรียก use case ต่อนะครับ หลังจากเรียก use case แล้วเนี่ย ตัว use case เนี่ยก็จะไปเรียกตัว repository ต่ออีกทีนึง

ให้มองว่า repository เนี่ยเป็น port นะครับ โดยที่เราจะมีตัว implement จริงๆ เนี่ย

ก็เป็น item Mongo repository เนาะ อันนี้คือถ้าจะเปลี่ยน Mongo จริงๆ ไปต่ออย่างอื่นก็ได้นะครับ แต่ว่าอันนี้ผมยกตัวอย่างเป็น Mongo ครับ

สมมุติเรารูปภาพไม่มา โอเคครับ

ก็สมมุติว่าเราเรียก controller เนี่ยมาเรียก use case get item by ID use case นะครับ เสร็จแล้วเนี่ย ตัว use case เนี่ยก็ไปเรียก item repository.𝚏𝚒𝚗𝚍𝙱𝚢𝙸𝚍 เนาะ คราวนี้ไอ้ตัวเนี้ย เนื่องจากว่าเราเวลาเราโยนของ เราก็จะคุยผ่าน interface ก็คือเป็น port เนาะ แต่ว่าตัว implement จริงเนี่ย เราก็คือเป็นตัว Mongo repository นะครับ ก็ 𝚏𝚒𝚗𝚍𝙱𝚢𝙸𝚍 แล้วก็ตัวเนี้ยมันก็จะไปต่อกับ ตัว database ที่เป็น Mongo ของเรา

Demo Code: Controller, Use Case, Repository, Interface8:59

อันนี้ไปเร็วมั้ยครับ

โอเค เดี๋ยวลองมาดูตัว demo code กันนะครับ

อันนี้เป็นตัว controller นะครับ จะเห็นว่าผมเป็น path item เนาะ

path item v1 นะครับ โดยที่ตัว controller เนี่ย เรามีการ constructor รอรับตัว get item by ID use case

เข้ามานะครับ เสร็จแล้วเนี่ย เราก็มี function นะครับที่เป็นเส้น get นะครับ ก็รับ param เป็น item ID เข้ามานะครับ หลังจากเราได้ item ID มาแล้วเนี่ย เราก็เอา item ID เนี่ยไปเรียก ไปโยนให้กับ use case อีกทีนึง ตัว use case เป็นคน execute นะครับ ตัว use case เนี่ย ก็จะเห็นว่าผมใช้ inject

ตัว item repository เข้ามานะครับเหมือนกัน โดยตัวเนี้ย ให้มองว่ามันเป็นแค่ interface โดยที่เราจะใช้ concrete class โดยการ binding เข้ามานะครับทีหลัง

หลังจากนั้นเราก็ execute นะครับ พอเรา execute ปุ๊บเนี่ย ก็จะเห็นว่า มีการไปเรียก repository 𝚏𝚒𝚗𝚍𝙱𝚢𝙸𝚍 ซึ่งให้มองว่ามันเป็นแค่ interface เนาะ แต่ว่าตัว implement จริงเราว่ากันทีหลังนะครับ

อันนี้ตัว interface ที่ผมบอกนะครับ อันนี้ผมลดโค้ดแล้วนะ เอาแค่เส้นเดียวก่อน เดี๋ยวโค้ดมันจะรกนะครับ ก็รับ item ID มานะครับ ก็เป็น promise เนาะ

ก็ maybe อาจจะได้ตัว item ออกไป หรือว่า undefined ออกไปนะครับ คราวนี้ ตัว Mongo repository เนี่ย เราก็ implement ตัว item repository ตัวนี้อีกทีนึงนะครับ ก็มีการผูกกับ framework เนาะ ก็อาจจะ inject เข้ามานะครับ เสร็จแล้วเราก็ไป implement จริงๆ ในนี้อีกทีนึง แต่เนื่องจากว่าเราเป็น hexagonal เนาะ ปกติแล้วเราต่อ database ออกมาเนี่ย มันจะได้ของที่เป็น object เหมือนของ database เนาะ แต่ว่าเวลาเรากลับเอามาใช้ในโค้ดเราเนี่ย ถ้าเรากลับมาดูรูปตรงนี้นะครับ

จะเห็นว่ามันคือ entity เนาะ เราพยายามใช้ของที่มันไม่ผูกติดกับ framework

หรือ database ใดๆ นะครับ

การ Map Object จาก Database กลับเป็น Domain Model และ Branded Type11:29

เพราะฉะนั้นน่ะ จะเห็นว่าตัวเนี้ยนะครับ ผมก็จะ map กลับมาเป็น domain นะครับ map กลับมาเป็น to domain นะครับ โดยที่ถ้าให้เห็นภาพนะครับ

underscore ID ตัวเนี้ย มันเป็น object ID ของตัว Mongo database นะครับ ผมก็มา 𝚝𝚘𝚂𝚝𝚛𝚒𝚗𝚐 นะครับ แล้วก็ as item ID ซึ่งอันนี้เนื่องจากว่า ไอ้คนที่เป็น COVID เนี่ย ตอนแรกผมจะเอาแบบ simple เนาะ เป็น as string ง่ายๆ เนี่ย เออ ไอ้คนที่เป็น COVID บอกว่า เอ้ย ขอใช้ branded type นะ แต่เจ้าตัวไม่อยู่นะครับ ไม่แน่ใจว่ารู้จัก branded type กันรึเปล่า เดี๋ยวไปดูโค้ดกันอีกทีนึงครับ โอเค เดี๋ยวผมเปิดโค้ดหมดเลยดีกว่า branded type

จริงๆ แล้วมันก็ อ่าว รูปเอาอีกแล้ว มามั้ย มาหน่อยครับ

Branded Type คืออะไร? เพิ่มความ Strict ให้กับ Type12:57

โอเคครับ จริงๆ แล้วลองไป search เรื่อง branded type ได้กันนะ ก็ดีนะครับ เพราะว่าจะทำให้เราแบบมั่นใจว่า

เราไม่ pass ของมั่ว ประมาณนั้นแหละครับ แต่ว่าก็สไลด์เรา เอ้ย โค้ดเราไม่ขึ้นนะครับ

ได้ข่าวว่าเป็น JavaScript Bangkok 2.0.1 แล้วนี่ครับ ใช่มั้ยครับ อันนี้ผมว่า .1 อาจจะยังไม่พอเนาะ โอเคครับ ก็จริงๆ แล้วอันนี้มันก็แค่เป็นทำแบรนด์ไทป์ขึ้นมานะครับ แต่ว่าไม่น่าจะว่าโค้ดเล็กไปรึเปล่า ผมซูมอีกหน่อย โอเค ข้างล่างผมย่อลงก่อน

ครับ

ตัวอย่างการใช้ Branded Type ใน Domain Model14:25

โอเค เราไปลองดูแบรนด์ไทป์กันนะครับ จริงๆ แล้วก็ไม่ได้มีอะไรเลยนะครับ ก็แค่เวลาเราพาสของมาเนี่ย เราจะไม่ได้พาสแค่ string เปล่าๆ เข้าไป เราก็แค่ strict type มันหน่อย เหมือนมี type เฉพาะขึ้นมาอีกนิดนึงนะครับ อันนี้ถ้าผมเปิดเต็มจอนี่มันจะดับอีกมั้ย โอ้โห รอดๆ โอเค อันนี้ให้เห็นนะครับว่าผมบอกว่ารอด เอาเลย

ครับ ก็จะให้เห็นว่าตัวที่เรารับเข้ามาเนี่ย ของที่ออกจาก database มาเนี่ย เราไม่ได้ใช้ตรงๆ นะ การที่ตัวในแอปพลิเคชันของเราเนี่ย use case ของเราเนี่ยจะใช้งานเนี่ย มันต้องเป็นโดเมนและมันไม่ใช่ item ที่เป็น type database

ข้อดีของ Hexagonal Architecture: เปลี่ยน Database ได้ง่าย15:16

เกิดวันดีคืนดีเนี่ย เราอยากจะเปลี่ยนของอะนะครับ อย่างเช่น ตอนแรกผมต่อ MongoDB อยู่ดีๆ บอก เอ๊ย ต้องการเปลี่ยนเป็น SQL อย่างเงี้ย คือข้อดีคือตัว use case ของเราเนี่ย แทบจะไม่เปลี่ยนเลย เราใช้ตัวเดิม เราแค่ไปเปลี่ยนตัวฝั่ง port ที่ implement

จากเปลี่ยนจาก MongoDB เป็น PostgreSQL ก็ได้นะครับ โค้ดเราถ้าเราเขียนแบบปกติอะเนาะ แบบอยู่ในก้อนเดียว อยู่ใน service เดียวเลยเนี่ย ตาม document ของ NestJS เนี่ย เราก็จะเจอปัญหาว่า โอ้โห วันดีคืนดีอยากจะเปลี่ยน database ไม่เอาแล้ว อันนี้แพง มีตัวที่ดีกว่า ตัวใหม่ออกมาอะไรอย่างเงี้ย โอ้ ตาย รื้อโค้ดพังทั้งแผงนะครับ

ปัญหา Caching ใน NestJS และ TTL16:04

โอเค เมื่อกี้ผมบ่นปัญหาไปเนาะ

เราเจอปัญหาอีกอย่างนึงครับ ตามที่ผมบอกเลยเมื่อกี้ database MongoDB Atlas โอ้โห

ยิ่งเจอแบบช่วงพีคๆ อะไรอย่างงี้ แบบ M50 เอาไม่อยู่ เปิด sharding แล้วด้วยอะไรอย่างเงี้ยนะครับ โอ้โห แบบราคาแบบคุยกับ business อะไรอย่างงี้ แบบเหงื่อตกเลยนะครับ แบบแพงๆ อะไรอย่างงี้เนาะ

จริงๆ แล้วเราก็ใช้พวก caching ได้เนาะ ไม่ต้อง hit database ทุกครั้งอะไรอย่างเงี้ยนะครับ จริงๆ ในตัว doc ของ NestJS เนี่ย ก็มีตัว cache manager แต่ว่ามันแคชให้เราก็จริง แต่ว่ามันไม่ได้มีวิธี invalidate cache ให้เราอะเนาะ คราวนี้ผมก็ยกตัวอย่างว่า อย่างเช่นผม find all find by ID ถ้าแบบมีการไป hit รอบนึงเนี่ย ผมต้องการจะแคชไว้

ถ้าใช้แคชปกติเนี่ย มันก็ cache manager ที่แบบสำเร็จรูปของเขามาเนาะ มันก็ใช้ได้แหละ แต่ว่าปัญหาคือ มีวันดีคืนดีต้องการ invalidate cache เนี่ย โอ้โห cache manager ไม่ตอบโจทย์แล้วแหละ

อย่างเช่นมีเคสอัพเดต มีเคส delete เข้ามาอะไรอย่างเงี้ย อันนี้ผมยกตัวอย่างให้เห็นภาพเนาะ แบบต้องการ invalidate cache เนี่ย มีอัพเดตเนี่ย แคชมันไม่อัพเดต ซึ่งปกติแล้วเราก็จะใช้เป็น TTL ถูกมั้ย เราอาจจะทำ time to live อะไรอย่างงี้ไว้นะครับ แต่ว่าพอมันเป็น TTL เนี่ย มันก็มีช่วงเวลาที่ data มันจะยังผิดอยู่

จนกว่า TTL จะหมดนะครับ

Solution แรก: แก้ที่ Use Case (ข้อดีข้อเสีย)17:52

first solution นะ ผมก็คิด โอเค

เอาไงดี คุยกับเพื่อนเนาะที่ไม่มาเพราะโควิดนี่แหละ

ครับ solution แรกเนี่ย เราเอาเป็นแบบ เราไปอยู่ใน use case มั้ย เลือกไปเขียน if else doc ไว้ใน use case อืม แต่มันก็มีข้อเสียนะ มันไม่ใช่ business logic ทำไมมันต้องไปอยู่ใน use case ในแอปพลิเคชันด้วย แล้วตัดปัญหาถัดไปนะครับ fix test case โอ้โห เรามี 100 200 use case 300 use case อย่างเงี้ย จริงๆ น่าจะเยอะกว่านั้นนะครับ ในโค้ดที่ทำงานกันอยู่เนี่ย โอ้ fix กี่ที่วะเนี่ย ไปเติม repo แบบต่อ Redis อะไรอย่างเงี้ย

แทนที่จะต่อ MongoDB ตรงเนาะ โอ้โห เพิ่มกี่ที่วะเนี่ย

แก้ test ไม่รู้ไม่จบไม่สิ้น อันนี้ลองมาดูตัวอย่างนะครับ แบบง่ายๆ เนาะ อันนี้อาจจะ run ไม่ได้จริงเนาะ เพราะว่าผมแค่ยกตัวอย่างให้เห็นภาพว่ามันประมาณนี้ ก็คือถ้าไป get ต่อ item cache repository เนี่ย

ไปต่อตัวนี้ ซึ่งตัวนี้เบื้องหลังเราอาจจะ implement เป็น Redis หรือตัวอื่นๆ ก็ได้นะครับ อาจจะเป็นแบบย้อนยุคไปหน่อยก็เป็น Memcached อะไรอย่างงี้เนาะ คราวนี้ตัวนี้ก็จะเห็นว่า ถ้าเราเจอ hit นะ ถ้ามันไม่ undefined หรืออะไรอย่างงี้ โอเค งั้นก็คืนไปเลย จบ แต่ถ้ามันไม่เจอก็ไปต่อ database ปกติ แล้วลองคิดดูว่าโค้ดตรงนี้อยู่ทุก use case เป็นหลายร้อยไฟล์นะครับ โอ้ แล้วผมเขียน test ด้วยไง โอ้โห unit test พังเป็นแผงแน่นอนแบบนี้ โอเค ก็เลย โอเค ไม่อยากแก้ use case

Solution ที่สอง: แก้ที่ MongoDB Repository (ข้อดีข้อเสีย)19:40

มันไม่ใช่ business logic ด้วย มันควรจะลองดูอีกท่าหนึ่งมั้ย

ไปเลือกใน MongoDB repository ก็ดี ผมปกติตัว repository ผมไม่ run unit test เนาะ

มันต่อ database อะไรอย่างงี้เนาะ เราไม่มี test เนาะ เพราะปกติผมแค่เขียนแค่อยู่ใน test นะครับ เล่าก่อน ก็แบบ เอ๊ย ไม่ต้องแก้ test เว้ย แต่ว่ามันก็เยอะอยู่ดีนะ ถ้าไปเขียน if อย่างงี้ แล้วพอมันไม่มี test อื้อหือ มันจะพังป่าววะ

อันนี้ก็ concept คล้ายๆ กับเมื่อกี้นะครับ แต่ว่าแค่ย้ายที่เนาะ solution ที่ 2 ที่คิดออกนะ ก็ย้ายที่มาอยู่ตรงนี้ อยู่ใน MongoDB repository แทน โอ้ ก็ยังไม่ตอบโจทย์อะ

ปวดหัว เอาไงดีวะ

Final Solution: ใช้ Decorator สำหรับ Caching20:41

แต่ว่าก่อนหน้านี้เราเขียน NestJS อยู่แล้ว decorator เต็มบ้านเต็มเมือง หรือว่า decorator มันตอบโจทย์เราวะ นะครับ ก็เป็น final solution ขึ้นมานะครับ ก็ไปแปะ decorator บนหัวของ method ใน MongoRepository นะครับ

แล้วก็เทสต์เก่าที่อยู่ใน use case เนี่ย เราไม่ต้องแก้เลยนะครับ เพราะว่ามันไม่เกี่ยวกันนะครับ แล้วก็เราสามารถทำ unit test สำหรับ decorator ได้ด้วยเนาะ ซึ่งอันนี้ผมไม่แน่ใจว่าเวลาจะพอหรือเปล่านะครับ ก็เดี๋ยวถ้ามีเวลาเดี๋ยวจะเปิดโค้ดให้ดูนะครับ แต่ว่าถ้าไม่มีเวลาไม่พอก็อาจจะไปดูใน git อีกทีนึงนะครับ

โค้ดที่ต้องแก้เนี่ยนิดเดียว แค่แปะๆ หัวไปหน่อยเดียว decorator แน่นอน เราไม่ต้องมานั่ง error

แบบถ้ามันไม่มีเทสต์ใน MongoRepository เนี่ย มันก็ไม่น่าจะ error เพราะว่าเราแค่แปะ decorator แต่เราเทสต์ที่ decorator เรียบร้อยแล้ว

Decorator สำหรับ Method ของ Class ทำงานอย่างไร21:47

เรามาดูรูปตัวอย่างก่อนนะครับ ก็ decorator เนี่ย concept มันเหมือนตกแต่งเพิ่มเติมเนาะ สมมุติเรา method มันคือ function เนาะ method คือ function ของ class นะ เป็น member ของ class นะครับ ให้เห็นว่า decorator เนี่ย เราก็จะมี original method อยู่ตรงกลางนะครับ แต่ว่าเราสามารถก่อนที่จะไปเรียกจริงๆ เนี่ย เราทำอะไรกับมันก่อนได้ หลังจากเรียกแล้วเราทำอะไรกับมันต่อได้เนาะ อันนี้ decorator สำหรับ method ของ class นะครับ

อันนี้มีตัวอย่างคล้ายๆ กันเลย เค้าทำ decorator memorize พอดีนะครับ

อันนี้ก็จะเห็นว่าเราเอา 𝚍𝚎𝚜𝚌𝚛𝚒𝚙𝚝𝚘𝚛.𝚟𝚊𝚕𝚞𝚎 มาเนี่ย อันนี้มันก็คือ original method นะครับ ของ class นั้นนะครับ แล้วก็เค้าก็ return descriptor ออกไป จริงๆ มัน .target หรือ propertyKey อาจจะต้องไปดู

แต่ว่าแต่ละแบบว่า decorator ของ class decorator ของ method ของ class เนี่ย มันเป็นยังไงนะครับ หรือว่าแค่ของ function ธรรมดาเนี่ย เป็นยังไง ก็ผมว่าไปดู doc ของ TypeScript ได้

อันนี้เป็นตัว implement จริง ผมไม่แน่ใจว่าเล็กมันเล็กไปหรือเปล่านะ เค้าก็ทำคล้ายๆ ถ้ามันเล็กไปเดี๋ยวผมเปิด มันจะดับหรือเปล่า

ตัวอย่าง Decorator Memorize23:10

ผมจะเปิดให้ดูนะครับ ก็ตัวเนี้ย ก็มีคนคิดคล้ายๆ ของเราเนาะ

ก็จะเห็นว่าเค้าทำเป็น memorize ขึ้นมาเนี่ย ก็เช็ก concept แบบคล้ายที่ผมคิดเลย

อารมณ์เดียวกันเด๊ะเลย ใช้ cache set อะไรอย่างนี้นะครับ แต่ว่าอันนี้เค้าไม่มีเรื่อง invalid cache เนาะ เดี๋ยวผมเล่าต่อ เสร็จแล้วเค้าก็เอา memorize มาแปะบนหัวนี้นะครับ ถ้าสมมุติเราเรียก fibo 2 รอบ รอบแรกอาจจะไม่มี cache เนาะ แต่ว่ารอบที่ 2 เนี่ย มี cache แน่นอนนะครับ รวมถึงจริงๆ แล้วตัว fibo เนี่ย มันเป็น recursive เนาะ จะเห็นว่ามันเป็น recursive ซึ่งอันไหนเคยคำนวณแล้วมันก็จะไม่คำนวณซ้ำนะครับ ก็เป็นเรื่อง performance นะครับ

Caching Decorator ทำงานอย่างไร24:06

จากโค้ดเมื่อกี๊เนาะ ผมก็แปลงมาเป็นรูปให้เห็นง่ายๆ เนาะ ถ้ามี cache แล้ว ถ้ามี cache

ถ้ามี cache ก็คืน cache ไปเลยนะครับ โดยไม่ต้องเรียก original method นะครับ

ถ้าเกิดไม่มี cache มาก่อนเนาะ ครั้งแรกก็จะต้องเรียกผ่าน original method เสร็จแล้วก็เอา cache ไป set นะครับ แล้วก็ return มันออกมานะครับ อันนี้ก็จะแบบ simple หน่อยนะครับ

Invalidating Cache Decorator ทำงานอย่างไร24:38

คราวนี้ ขา invalid cache ที่ผมคิดไอเดียขึ้นมาเนี่ย ก็คล้ายๆ กันนี่ ก็ delete cache with combination key เดี๋ยวอันนี้ผมเล่าต่อเนาะ หลักๆ ก็คือไป delete cache หลังจากเราอัพเดตหรืออะไรหรือ create ใหม่ขึ้นมาเนี่ย เราก็ไป invalid มันซะ มันจะได้ยิง request เข้ามาแล้วก็ เดี๋ยวมัน hit รอบใหม่ให้เราเองนะครับ

ตัวอย่าง Key Combination สำหรับ Caching25:01

อันนี้ผมยกตัวอย่าง key ที่ผมพูดไปเมื่อกี๊เนาะ อันนี้เป็นทีมผมทำเนาะ ก็ถ้าสมมุติว่ามี all key find all อย่างเงี้ย ผมก็แปะ all key ให้เลย เกิดส่ง ID มาเป็น 1 ก็เราจะ cache key ชื่อนี้ไว้นะครับ หรือว่าแบบส่ง status available color แบบเป็น 2 key เข้ามาในการ search หาอะไรอย่างเงี้ย เราก็แปะอย่างนี้ได้นะครับ อันนี้ก็เหมือนกันก็ใช้ 2 key นะครับ ก็คือ country กับ status ในการหาอะไรอย่างนี้ก็ได้นะครับ

Demo Code: Repository, Base Key, Key Combination25:37

เดี๋ยวเราไปดูโค้ดเรากันดีกว่านะครับ

ผมให้ดูตัว repository ก่อนนะครับ

อันนี้ผมปิดตรงข้างล่างก่อน

จะเห็นว่าผมมี base key อยู่นะครับ แล้วก็มี key combination เกิดเราสร้างของใหม่ขึ้นมาเนี่ย เราก็จะมา invalid แก๊งพวกนี้นะครับ เนื่องจากว่าเรามี search หลายแบบนะครับ มี search find all มี search ด้วย ID มี search ด้วย status กับ color นะครับ มี search ด้วย country กับ category นะครับ

ก็จะเห็นว่าเราผมทำไว้อย่างเงี้ย ก็คือเราสามารถ search ของได้นะครับ แล้วก็เกิดวันดีคืนดีเรามีอัพเดตหรือมี create ของใหม่ขึ้นมา จริงๆ อันนี้อาจจะต้องมาดูเรื่อง field ละเอียดๆ หน่อยอีกทีนึงนะครับ แต่ว่าอันนี้ผมทำแบบง่ายๆ ให้เห็นภาพกันว่า สมมุติว่ามีเคสอัพเดตของเข้ามาเนี่ย

สมมุติว่าเป็นเราเคย search ด้วย country กับ category อันเนี้ย ไอ้ตัวเนี้ยจะโดน invalid ทีเดียวเลยนะครับ

Demo Code: Caching Decorator Implementation26:56

เดี๋ยวเราไปดูตัว decorator ของเรากันนะครับ

อันนี้ก็จะเห็นว่ามันที่ไม่พอหรือเปล่าครับ

เนี่ยเราก็จะเก็บ original method ไว้ก่อนนะครับ เสร็จแล้วเราก็ override ไอ้ตัว method เนี่ย

ด้วย function ที่ wrap เข้าทั้งก้อนเลยนะครับ เสร็จแล้วผมก็มีไป gen key ขึ้นมานะครับ gen key นี่คืออะไร gen key น่าจะเด้งไปอีกทีนึง แต่หลักๆ ก็คือเป็นเหมือนอันเนี้ยนะครับ

ก็คือแบบ gen ขึ้นมาแล้วก็เอา value ของตัวเองแปะเข้าไป

อันนี้ก็เช็คว่าถ้าไป get มาแล้วเจอ cache key เนี่ย ไอ้ตัว cache key ตัวเนี้ยเจอมาปุ๊บก็คืนออกไปเลยนะครับ

โอเค แต่ถ้าไม่เจอเราก็จะเรียก original method ก่อน 1 ทีนะครับ โดยที่ตัวเนี้ยจะเห็นว่าผมใช้เป็น domain หรือ array ของ domain เนาะ ซึ่งอันเนี้ยผมรับเป็นตัว Redis mapper เข้ามาตัวนี้นะครับ ก็คืออันนี้ก็คือเป็น model จากต้นทางเนาะ เวลาเราได้ของจาก database หรืออะไรมาเนี่ย มันจะเป็นแบบ entity ของ Mongo หรือเป็นแบบ type ที่มันยังไม่พร้อมใช้งานในระบบเราเนี่ย ก็จะเป็นตัวนี้ก่อน แล้วก็เราบอกว่าเดี๋ยวเราจะ convert มาเป็นตัว domain ในระบบของเราอีกทีนึงนะครับ

เสร็จแล้วเราก็โอเค เราก็ได้ resource มาแล้ว เราก็ไป map domain to cache อีกทีนึงนะครับ map domain to cache อยู่ข้างล่าง ก็จะจริงๆ แล้วก็ไม่มีอะไร ถ้ามันได้ของออกมาเป็น array

มันก็จะไป map เป็น array ออกมา ถ้ามันไม่ใช่ array เราก็จะ map เป็น domain model ธรรมดา หรือ undefined ออกไปนะครับ

Demo Code: Setting and Getting Cache28:55

เสร็จแล้วก็ไป set cache key นะครับ

Demo Code: Unit Test สำหรับ Decorator29:02

โดยทั้งหมดเนี่ยผมก็ run test

อันนี้ก็จริงๆ อันนี้ก็จะมี test อยู่เหมือนกันนะครับ test ผมก็ยกตัวอย่างเช่นตัวนี้ spy counter

โอเค spy counter เนี่ยครับ ถ้าเราเรียก find all เนี่ย 3 ครั้งเนี่ย มันจะต้องไปคิดแค่เรียกไอ้ตัวจริงๆ เนี่ย ไอ้ตัว original method แค่ครั้งเดียวนะครับ ก็จริงๆ ก็ทำไว้หลายเคสนะครับ แต่ว่าผมคงไม่เล่าหมดนะครับ

ให้ดูอันนี้อีกทีนึงครับ find by ID เนี่ย แล้วผมก็ทำ spy ไว้อีกทีนึงนะครับ ทำ test class ขึ้นมา จริงๆ แล้วอันนี้ผมใช้ตัวช่วยหลายอย่างเลยเหมือนกันนะครับ

before each ผมอยู่ไหน อันนี้ผมใช้ testing module ของ NestJS นะครับ แต่ว่าอีกตัวนึงผมว่า point สำคัญของเราตอนนี้น่าจะไม่ใช่ตัวนี้ละ เราไปดูตัว invalidate cache key ดีกว่า ก็หลักๆ แล้วขอโทษครับ น่าจะต้องไปดูตัว implement มันก่อนเนาะ

Demo: ยิง Request ให้ดูการทำงานจริง30:27

ให้ดูอีกทีนึงครับ เวลาจะ invalidate cache เนี่ย ผม invalidate ทั้งปึกนี้เลยนะครับ เดี๋ยวจะไม่ทัน ผมกลัวเลทมากเลย เดี๋ยวผมยิง request ให้ดูดีกว่านะครับ

โอเค มาถึงผมจะลองยิง terminal ด้วย

โอเค อันนี้จะเห็นว่ายิงไปนะครับ ผมพ่น log debug เอาไว้นะครับ ก็เป็น not hit นะครับ

อันนี้จะเห็นว่ามันไม่ hit เนาะ แล้วก็อันนี้คืนของว่างเปล่ามาเลยนะครับ เสร็จแล้วผมไปยิง create item 1 นะครับ

ชื่อ Red เป็น Red นะครับ เสร็จแล้วผมไปลอง get all อีกทีนึงนะครับ ก็จะเห็นว่า cache not hit อยู่เนาะ อันนี้ผมตั้ง delay ไว้ให้เหมือนแบบจำลอง เหมือนแบบต่อ database แล้วมันช้าอะไรอย่างนี้นะครับ

เนี่ยครับก็จะเห็นว่าอันเนี้ยเราคืนมาก้อนนึง และถ้าผมยิงอีกรอบนึงก็จะเห็นว่ามัน hit ละ

ยิงรอบนี้ไม่เจอ ยิงอีกรอบนึงเจอเลยนะครับ คราวนี้ผมไปสร้างตัวสีฟ้าเป็นตัวที่ 2 ขึ้นมานะครับ ผมกลับมายิง get all นะครับ

ก็จะเห็นว่ามันไม่เจอนะครับ

ผมก็ยิงอีกรอบ รอบนี้เจอละ

โอเค ก็จะเห็นว่ายิงรอบนึงมันไม่เจอเนาะ

เวลาเรายิงไปเนี่ยมันจะมี invalidate cache นะครับ ซึ่งจริงๆ แล้วอันนี้ผมก็ทำไว้หลายแบบเหมือนกันนะครับ อันนี้ผมยิง get by ID ยิงรอบแรกไม่เจอนะครับ ปึ๊บ ยิงรอบ 2 hit นะครับ

สมมติผมไปยิงเคสอื่นบ้างนะครับ

โอเค เอาเคส search ด้วยทั้ง country แล้วก็ category นะครับ

ยิงรอบแรกไปนะครับ ไม่เจอนะครับ เสร็จแล้วผมยิงอีกรอบนึงนะครับ ปึ๊บ คราวนี้มันก็จะ hit ละ

คราวนี้ลองไป update ดูนะครับ ผมไป update เดี๋ยวผมดึงตรงนี้ลงหน่อยดีกว่า ไม่เกิดประโยชน์เลย ผมขอ get all อีกครั้งนึงครับ get all ปั๊บ อันนี้เนื่องจากว่ามันไม่ cache hit นะ

มันน่าจะต้องรอสักแป๊บนึงนะครับ ก็จะขึ้นมา 2 ตัวแล้วเนาะ โอเค ผมยิงอีกรอบนึง

ปิดอันนี้ด้วย จะได้เห็นชัดๆ ปึ๊บ ขึ้นเลยนะครับ ไม่ต้องรอต่อ database จากการ mock ไว้ เสร็จแล้วเดี๋ยวผมไปยิง update นะครับ ยิง update ตัวแรกนะครับ ปั๊บ อันนี้อาจจะช้าหน่อยเพราะว่ามีการไป find นิดนึงนะครับ

status available โอเค

อันนี้ผมยิง get all item อีกครั้งนึงครับ อันนี้ก็จะเห็นว่ามันไม่เจอเนาะครับ ก็เลยช้าหน่อย ก็จะเห็นว่าตัว name เนี่ยมันเปลี่ยนเป็น JavaScript Bangkok 2.0 แล้ว แล้วก็ status เป็น unavailable เรียบร้อยแล้วครับ จากการ update ก่อนหน้านี้นะครับ

ถ้าสมมติผมยิง get all อีกรอบนึงมันน่าจะต้องไวละ ปั๊บ hit นะครับ ปิดๆๆ อันนี้ก่อน จะได้เห็นชัดๆ ครับ ยิงปึ๊บขึ้นเลยนะครับ เพราะติด cache นะครับ

สรุปและข้อจำกัดของ Caching Decorator34:34

โอเค เดี๋ยวอันนี้ผมสรุปนะครับ เพราะว่ากลัวจะไม่ทันเวลากัน ตัวนี้นะครับ ก็จะเห็นว่ามันค่อนข้างจะใช้งานได้ระดับนึง เราสามารถ invalidate cache ได้ โดยที่ปกติแล้วเนี่ย ถ้าเราใช้ cache manager ปกติเนี่ย มันจะทำอะไรแบบนี้ไม่ได้นะครับ แล้วก็อันนี้ สังเกตดูว่า code ผมอ่ะ ถ้ามาดูตัว repository ของผมเนี่ย ผมไม่ต้องไปแก้ไขมันเลยนะครับ ผมแค่เติม decorator ไว้ข้างบนหัวมันนิดนึงนะครับ แต่ว่าอาจจะมีข้อจำกัดอยู่บ้างนะครับ ข้อจำกัดที่ว่าก็คือ find key ที่ใช้ในการ find เนี่ย

ถ้ามันเปลี่ยนเนี่ย มันก็จะยังไม่อัพเดทนะครับ

อธิบายไม่เห็นภาพ ผมว่าเรายิงให้ดูดีกว่า

โอเค ผมขอรีทีนึง จะได้ clear ของ โอเคครับ เดี๋ยวผมยิงใหม่นะครับ ปึ๊บ สีแดง สีฟ้านะครับ

ยิง get all รอซิ มายัง

โอเค มา 2 ตัวละ

สมมติว่าผม search ด้วย status available นะครับ กดไปรอบแรกเนี่ย โอเค ก็จะได้ของมา 2 ตัวตามนี้นะครับ แล้วผมไป update มันเป็น unavailable เนี่ย แล้วผมก็กลับมา get by state get ด้วยอันนี้อีกทีนึง

ซึ่งอันนี้ อ้าว จอดับผมก็ไม่รู้ตัว

ปัญหา Key ที่ใช้ในการ Search เปลี่ยนแปลงได้36:15

โอเค เดี๋ยวกัวเกินเวลาครับ ผมว่าเดี๋ยวไปอ่าน code ดีกว่า แต่ว่า point สำคัญคือมันจะไม่ได้ key ที่ใช้ในการ search อ่ะครับ ไม่ควรจะเปลี่ยนแปลงได้ครับ สำหรับอันนี้ที่เป็นปัญหาอยู่นะครับ แต่ว่างานที่ผมใช้อยู่ปกติ key มันค่าค่อนข้างจะคงที่อยู่แล้ว ก็เลยไม่เจอปัญหาที่ว่านะครับ solve problem ของผมอย่างจริงจังนะครับ ก็ไม่เจอปัญหานะครับ

ผลลัพธ์หลังจาก Implement Caching Decorator: Load ลดลง, ราคาลดลง36:41

ก็ load size database ไปได้เยอะ

ราคาครึ่งนึงได้อ่ะครับ หลังจากมาใช้ Redis ไปนะครับ ก็อันนี้ใกล้จบครับ

ช่วงขายของ: Course RESTful API by NestJS36:54

จริงๆ ผมมี course อยู่กับทาง born to dev นะครับ ก็เป็น RESTful API by NestJS นะครับ ก็ใช้กับ MongoDB แล้วก็เป็น hexagonal architecture นะครับ ครับ โอเค แล้วก็มี อ้าว รูปไม่ขึ้น

โอเคครับ มีเพจนะครับ ก็ jitrak.dev นะครับ เขียน blog อยู่บ้างเหมือนกันครับ

Q&A: REST Client Plugin ใน VS Code37:18

โอเคครับ Q&A ครับ

ที่ใช้กดยิง request ครับ อันนี้คือต้อง dev อะไรเพิ่มมั้ยครับ หรือมี plugin ลงได้เลย อันนี้ใช้ REST Client ของ VS Code ได้เลยครับ เป็น plugin ตัวนี้ครับ

อันนี้เล่าให้เนาะ เผื่อใครไม่รู้อ่ะครับ ก็ไอ้ตัวนี้มันทำให้เราสามารถยิง request ได้ง่ายนะครับ คือปกติแล้วผมว่าหลายคนก็คงจะไปใช้พวก Postman หรืออะไรอย่างงี้กันเนาะ ซึ่งผมไม่ค่อยชอบ Postman นะครับ ข้อเสียของมันคือมันไม่ได้อยู่ใน Git อยู่ใน source code เราอ่ะ ผมต้องการให้มันอยู่ใน source code ผมก็เลยใช้ REST Client ในการ save ตัว request เอาไว้ โอเคครับ ครับ น่าจะไม่มีคำถามแล้วครับ ครับ ขอบคุณครับ ครับ ขอบคุณครับ