บทเรียนที่ 18: การโต้ตอบกับบริการภายนอกผ่าน Webhook
Webhook คือฟังก์ชัน callback ที่ทำงานบน HTTP ซึ่งบริการภายนอกจะเรียกใช้เพื่อแจ้งเตือนเหตุการณ์บางอย่าง พร้อมส่งเพย์โหลดของข้อมูลไปด้วย Webhook ช่วยให้เว็บแอปและบริการต่าง ๆ สื่อสารกันได้
ตัวอย่างของบริการที่เรียกใช้ Webhook ได้แก่:
- GitHub เมื่อมีการ push commit ไปยัง repository
- Dropbox เมื่อเอกสารถูกอัปเดต
- WooCommerce เมื่อมีการเพิ่มคำสั่งซื้อ
- Microsoft Teams เพื่อรับข้อความ rich text และโพสต์ในช่องสาธารณะ
- ConvertKit เมื่อสมาชิกถูกเปิดใช้งาน
ด้วย Gato GraphQL เราสามารถสร้าง Persisted Queries ที่ทำหน้าที่เป็น Webhook ได้:
- เนื่องจาก Persisted Query ถูกเปิดเผยภายใต้ URL ของตัวเอง จึงสามารถใช้เป็นเป้าหมายของ Webhook ได้
- Persisted Query แต่ละรายการสามารถจัดการ Webhook เฉพาะรายการได้โดยเฉพาะ
ในบทเรียนนี้ เราจะสร้าง Persisted Query เพื่อโต้ตอบกับ ConvertKit
การเรียกดูเอกสารของ Webhook
ขั้นตอนแรกคือการอ่านเอกสารของเว็บไซต์ และทำความเข้าใจว่าข้อมูลใดบ้างที่ถูกส่งมาผ่านเพย์โหลด
เมื่อวิเคราะห์ webhooks ใน ConvertKit เหตุการณ์ที่เกี่ยวข้องกับสมาชิกจะส่งคำขอ POST มายัง URL ของเราพร้อมเพย์โหลด JSON แบบนี้:
{
"subscriber": {
"id": 1,
"first_name": "John",
"email_address": "John@example.com",
"state": "active",
"created_at": "2018-02-15T19:40:24.913Z",
"fields": {
"My Custom Field": "Value"
}
}
}การดึงข้อมูลออกจากเพย์โหลด
เนื่องจากคำขอถูกส่งมาผ่าน POST เราจึงต้องดึงข้อมูลออกจากเนื้อหา (body) ของคำขอ HTTP (ซึ่งรองรับผ่านส่วนขยาย HTTP Request via Schema)
หากคำขอ HTTP ถูกดำเนินการผ่าน GET Persisted Query จะสามารถดึงรายการข้อมูลจากพารามิเตอร์ของ URLได้โดยตรง
GraphQL query นี้จะดึงเนื้อหาของคำขอและแปลงเป็นออบเจ็กต์ JSON จากนั้นจะดึงรายการ "subscriber.first_name" และ "subscriber.email_address" ออกจากออบเจ็กต์ JSON และส่งออกเป็นตัวแปรแบบไดนามิก:
query ExtractPayloadData {
body: _httpRequestBody
bodyJSONObject: _strDecodeJSONObject(string: $__body)
subscriberFirstName: _objectProperty(
object: $__bodyJSONObject,
by: { path: "subscriber.first_name" }
)
@export(as: "subscriberFirstName")
subscriberEmail: _objectProperty(
object: $__bodyJSONObject,
by: { path: "subscriber.email_address" }
)
@export(as: "subscriberEmail")
}ส่วนขยาย HTTP Request via Schema ช่วยให้เราดึงข้อมูลทั้งหมดของคำขอ HTTP ปัจจุบันได้ ผ่านฟิลด์ต่อไปนี้:
_httpRequestBody: เนื้อหาของคำขอ HTTP_httpRequestClientHost: โฮสต์ของไคลเอนต์_httpRequestClientIP: ที่อยู่ IP ของไคลเอนต์ (หรือnullหากเซิร์ฟเวอร์ตั้งค่าไม่ถูกต้อง)_httpRequestCookie: ค่า Cookie ของคำขอ_httpRequestCookies: Cookie ของคำขอ_httpRequestDomain: โดเมนของ URL ที่ร้องขอ_httpRequestFullURL: URL ที่ร้องขอ (รวมพารามิเตอร์ query)_httpRequestHasCookie: คำขอมี Cookie ที่ระบุหรือไม่?_httpRequestHasHeader: คำขอมี header ที่ระบุหรือไม่?_httpRequestHasParam: คำขอมีพารามิเตอร์ที่ระบุหรือไม่?_httpRequestHeader: ค่า header ของคำขอ_httpRequestHeaders: header ของคำขอ_httpRequestHost: โฮสต์ของ URL ที่ร้องขอ_httpRequestMethod: เมธอดของคำขอ_httpRequestStringParam: ค่าของพารามิเตอร์ (ส่งผ่าน POST หรือ GET) ในรูปแบบ?param=value_httpRequestStringListParam: ค่าของพารามิเตอร์ (ส่งผ่าน POST หรือ GET) ในรูปแบบ?param[]=value1¶m[]=value2_httpRequestParams: พารามิเตอร์ที่ส่งผ่าน POST หรือผ่าน query ของ URL_httpRequestProtocol: โปรโตคอลของคำขอ_httpRequestQuery: สตริงของพารามิเตอร์ query_httpRequestReferer: referer ของคำขอ_httpRequestRequestTime: timestamp ของจุดเริ่มต้นของคำขอ_httpRequestScheme: scheme ของ URL ที่ร้องขอ_httpRequestServerIP: ที่อยู่ IP ของเซิร์ฟเวอร์_httpRequestURL: URL ที่ร้องขอ (ไม่รวมพารามิเตอร์ query)_httpRequestURLPath: พาธสัมบูรณ์ (ขึ้นต้นด้วย "/") ของ URL ที่ร้องขอ_httpRequestUserAgent: User agent
การดำเนินการบางอย่างกับข้อมูล
เมื่อเราดึงข้อมูลออกจากเพย์โหลดแล้ว เราก็สามารถดำเนินการบางอย่างกับข้อมูลนั้นได้
GraphQL query นี้จัดการกับเหตุการณ์ subscriber.subscriber_unsubscribe เพื่อส่งอีเมลไปยังผู้ที่ยกเลิกการสมัคร เพื่อขอความคิดเห็น
query CreateEmailMessage
@depends(on: "ExtractPayloadData")
{
emailMessageTemplate: _strConvertMarkdownToHTML(
text: """
Hey {$subscriberFirstName}, it's sad to let you go!
Please be welcome to complete [this form](https://forms.gle/FpXNromWAsZYC1zB8) and let us know if there is anything we can do better.
Thanks. Hope to see you back!
"""
)
emailMessage: _strReplaceMultiple(
search: ["{$subscriberFirstName}"],
replaceWith: [$subscriberFirstName],
in: $__emailMessageTemplate
)
@export(as: "emailMessage")
}
mutation SendEmail @depends(on: "CreateEmailMessage") {
_sendEmail(
input: {
to: $subscriberEmail
subject: "Would you like to give us feedback on how we can improve?"
messageAs: {
html: $emailMessage
}
}
) {
status
}
}