สัญญา(ไม่)ปากเปล่า - data contracts in action (NodeJS)
Data contract เป็นส่วนนึงของ data integration โดยตัวมันเองทำหน้าที่เป็นเหมือน “contract” หรือสัญญา ว่าเราจะรับส่งข้อมูลกันระหว่างต้นทางและปลายทางยังไงบ้าง เพื่อสร้าง pipeline ได้อย่างถูกต้อง และโปร่งใสฮะ
ทีนี้ ตัว data contract เองจะต้องอธิบายได้ว่ามีอะไรบ้าง ตามนี้
- Responsible persons (ใคร)
- Data subjects (ส่งอะไร)
- Methods and channels (ส่งทางไหน)
- Frequency (ส่งตอนไหน)
- Data schemas (ส่งด้วยรูปแบบอะไร)
เลยกลายมาเป็น blog เล่าเรื่อง basic data contract extraction ตอนนี้ฮะ
Data contract as an API
วาด flow คร่าวๆ เวลาเรา integrate data contracts เข้าไปใน ETL ของเรา แบบนี้ฮะ
สีส้มๆ ตรง "validation" นี่แหละ คือ หัวใจสำคัญของ data contract เลยฮะ เพราะ "สัญญา" ไว้แล้วว่าจะส่งอะไร แบบไหน เราเลยต้องมี validation เข้ามาตรวจสอบว่าอะไรที่ตรงตามสัญญาบ้าง อันไหนไม่ถูกต้องก็จะเก็บไว้อีกที่นึงเพื่อตรวจสอบหรืออะไรก็ว่าไป
ก่อนจะไปหัวข้อถัดไป
blog นี้ จะเล่าถึง real-time integration เป็น API ด้วย NodeJS และใช้ AJV เพื่อทำ validation
สำหรับ Python สามารถอ่านได้ตามลิงก์นี้ฮะ
API swagger
API swagger เป็นหัวข้อพื้นฐานของการทำ API ฮะ มันเป็นเหมือนห้องสมุด เพื่อแสดงว่า API ของเรามีหน้าตายังไง รับอะไร ส่งอะไร โดยใช้ configuration file ที่เราสามารถเขียนด้วย YAML หรือ JSON ก็ได้ ตรงนี้ เราจะใช้ YAML กันฮะ
และก็เขียนด้วย VSCode
1. VSCode plugin: OpenAPI Editor
ผมเลือกใช้ plugin ใน VSCode ตัวนี้ ช่วยเขียน API definition และสามารถ review swagger ได้ง่ายมากๆ ฮะ
2. เขียน YAML file
เมื่อเราติดตั้งและเปิดใช้ plugin ใน VSCode แล้ว ให้เราเปิด palette แล้วเลือก “create”
กดปุ๊บ มันจะสร้าง OpenAPI YAML file หน้าตาแบบนี้มาให้
มันมีปุ่ม preview ก็จิ้มจึ้กไปทีนึง มันจะแสดงหน้า swagger มาแบบนี้
3. สร้าง path ใหม่
เอาล่ะ ลองสร้าง path ใหม่ ให้ชื่อว่า people
จากนั้นก็ save ด้วยชื่อ people.yml
.
จริงๆ มันมีรายละเอียดการเขียน OpenAPI configuration file อยู่ เดี๋ยวค่อยเล่าวันหลังนะฮะ
เขียน app พร้อม validation
เรามี API configuration ที่ชื่อ people.yml
แล้ว ซึ่งนั่นแหละ คือ contract ของเรา ทีนี้ เราต้องการจะรับข้อมูล people เข้ามา และให้มี "validation" ด้วย เราจะเริ่มจากสร้าง API ให้คนอื่นสามารถ POST
ข้อมูลอะไรสักอย่างเข้ามาได้ แล้วค่อย "validate" ตอน extract ฮะ
Validation หมายถึง การที่เราตรวจสอบว่าข้อมูล (request payload) ที่รับเข้ามาเนี่ย มันถูกต้องหรือเปล่า เช่น field นี้ต้องเป็น string ส่วน field นั้นต้องเป็น integer อะไรแบบนี้
เราจะใช้ตัวช่วย คือ AJV เพื่อทำ validation ให้เราฮะ
1. สร้าง javascript app
ตั้งต้น index.js
ด้วย
และใช้ package.json แบบนี้
เมื่อ npm install
แล้ว เราจะมี app ที่ listen port 4001 พร้อมมี endpoint /people
2. อ่าน contract
เราจะใช้ js-yaml
library อ่าน people.yml
3. Setup AJV
เราได้ข้อมูล contract people.yml
ก็เพิ่มเข้าใน AJV
strict: false
เพื่อ disable restrictions ไม่ให้ throw error ถ้ามี keyword หรือ undefined formataddFormat()
เพื่อ include formats ที่กำหนดด้วย JSON Schema specification.AJV.addSchema(contract, key)
เพื่อเพิ่ม contract ที่เราต้องการพร้อมกำหนด key
4. Validate
เราจะมี request payload ใน path /people
เราก็ทำ validation ข้างใน path นั้น
AJV.compile()
เพื่อ setup function สำหรับการทำ validation ของแต่ละ contract$ref: ...
เอาไว้เระบุว่า เนื้อหาของ contract ที่จะ validate นั้นมันอยู่ตรงไหน ของตัวอย่างนี้ คือ เรากำลัง validate ของ contract keypeople
ตามด้วย#
หมายถึง internal reference และ definition ของ contract ซึ่งอยู่ที่ path/components/schemas/people
validator()
อันนี้เป็น function ที่เราสร้างขึ้นเองจากAJV.compile()
และจะใช้อันนี้แหละเพื่อ validate- ถ้า
validator()
return "true" แปลว่า payload ถูกต้องตามที่ระบุใน contract - แต่ถ้า
validator()
return "false" แปลว่า payload มีอะไรสักอย่างผิดไปละ ตรงนี้ เราสามารถดูได้ว่าผิดตรงไหน จาก property.errors
5. Complete API
เอาทุกอย่างมารวมเข้าด้วยกัน
6. Test
จะใช้ curl แบบนี้
หรือ http tool ที่ถนัดก็ได้ฮะ
กรณี payload ถูกต้อง
กรณี payload มีบาง field ระบุ type ไม่ถูกต้อง
กรณี payload ไม่มี required field
ถ้ามีหลาย contract ?
จะ validate contract หลายๆ ตัวยังไงดี?
สมมติว่า เรามี endpoint 2 อัน คือ people
กับ pets
และก็จะมี contract file 2 file เหมือนกัน เราจะต้อง optimize validation กันหน่อยฮะ
people
contract เรามีอยู่แล้ว ส่วนอันนี้คือ pets
contract
สามารถเขียนได้แนวๆ นี้นะฮะ
- List contract files แล้วทำ
AJV.addSchema()
ทีละ file โดยกำหนด contract key ตามชื่อของ file - เขียน generic function ให้รับ contract key (
contract_key
) และ request payload (body
) และสุดท้าย response object (res
) - ข้างใน generic function เขียนเหมือนเดิม คือ มี compile, validate, check แต่ปรับตัวแปรนิดหน่อยให้เป็น generic
- ทีนี้ ในแต่ละ endpoint
/people
กับ/pets
ให้ไปเรียก generic function โดยส่งค่าpeople
กับpets
ไปเป็น contract key ตามลำดับ
complete code จะประมาณนี้ฮะ
ลอง test case ของ pets
ให้มันเกิด error ซิ
Code repo
References
- A guide to data contracts https://www.striim.com/blog/a-guide-to-data-contracts/
- OpenAPI (Swagger) Editor https://marketplace.visualstudio.com/items?itemName=42Crunch.vscode-openapi
- OpenAPI initiative https://www.openapis.org
- AJV main page https://ajv.js.org
- AJV format https://ajv.js.org/packages/ajv-formats.html
- AJV strict mode https://ajv.js.org/strict-mode.html#prohibit-ignored-keywords