บล็อก

🛠 WordPress ควรมี GraphQL API ใน core หรือไม่?

Leonardo Losoviz
โดย Leonardo Losoviz ·

WordPress 5.7 กำลังจะเปิดตัวเร็วๆ นี้ เช่นเดียวกับหลายๆ รุ่นที่ผ่านมา WP REST API ก็จะมีฟีเจอร์ใหม่หลายอย่าง เพิ่มเข้ามาด้วย

ในบรรดาฟีเจอร์ใหม่ มีอยู่หนึ่งอย่างที่ดึงดูดความสนใจของผม: "Image Editor Accepts a List of Modifiers"

endpoint /wp/v2/media/<id>/edit ที่แนะนำใน WordPress 5.5 มาพร้อมกับ API ที่จำกัด ซึ่งรับรองการหมุนและการครอบตัดระดับบนสุดเท่านั้น ใน 50124 API นี้ได้รับการทำให้มีประสิทธิภาพและยืดหยุ่นมากขึ้นด้วยการรับ array ของการปรับแต่งผ่าน request parameter ใหม่ชื่อ modifiers

import apiFetch from '@wordpress/api-fetch';
 
const data = {
  modifiers: [
    {
      type: 'crop',
      args: {
        left  : 0,
        top   : 0,
        width : 80,
        height: 80
      }
    },
    {
      type: 'rotate',
      args: {
        angle: 90
      }
    }
  ]
};
apiFetch( { data, method: 'POST', path: '/wp/v2/media/5/edit' } );

การพัฒนานี้ใช้เวลานานพอสมควร

ก่อนอื่น ใน WordPress 5.5 ได้มีการแนะนำ endpoint สำหรับแก้ไขรูปภาพ

endpoint นี้ในตอนแรกค่อนข้างตายตัว โดยต้องส่งข้อมูลทั้งหมดที่เกี่ยวกับการดำเนินการทั้งหมดที่จะใช้กับรูปภาพพร้อมกัน ตัวอย่างเช่น เพื่อหมุนรูปภาพและปรับขนาด เราต้องส่งข้อมูลแบบนี้:

{
  "x": 0,
  "y": 0,
  "width": 80,
  "height": 80,
  "rotate": 90
}

จากนั้น ใน WordPress 5.6 ได้มีการแนะนำ batch operations ให้กับ WP REST API

สุดท้าย ใน WordPress 5.7 ที่กำลังจะมาถึง การดำเนินการที่จะใช้กับรูปภาพได้ถูกแยกออกจากกัน ทำให้เรามีการดำเนินการ "crop" และ "rotate" แยกกัน การดำเนินการเหล่านี้สามารถทำได้ด้วยตัวเอง แต่ยังสามารถทำร่วมกันใน request เดียวกันผ่าน batching

ดังที่เห็นก่อนหน้านี้ การส่งข้อมูลไปยัง endpoint ตอนนี้ดูสง่างามกว่ามาก:

{
  "modifiers": [
    {
      "type": "crop",
      "args": {
        "left"  : 0,
        "top"   : 0,
        "width" : 80,
        "height": 80
      }
    },
    {
      "type": "rotate",
      "args": {
        "angle": 90
      }
    }
  ]
}

กำลังทำซ้ำสิ่งที่มีอยู่แล้ว?

WP REST API ไม่ใช่ API เดียวสำหรับ WordPress มีทางเลือก (อย่างน้อย) สองอย่าง:

  • GraphQL ผ่าน WPGraphQL
  • GraphQL + persisted queries ผ่าน Gato GraphQL
    (☝🏽 นี่คือผม เจ้าภาพของบล็อกโพสต์นี้ ☝🏽)

GraphQL เป็น API ประเภทใหม่ที่โดดเด่นในเรื่องการทำ batch operations หากใช้ GraphQL ก็ไม่จำเป็นต้องเสียเวลาและพลังงานในการพัฒนาโซลูชันแบบกำหนดเองสำหรับสิ่งเหล่านี้ เหมือนกับที่ต้องทำกับ REST

แท้จริงแล้ว REST อาจมองได้ว่า "คัดลอก" ฟีเจอร์นี้มาจาก GraphQL

REST คัดลอก GraphQL?

การรองรับ batch operations ใน WP REST API ใช้เวลาอย่างน้อย 2 อาจถึง 3 รอบการออกรุ่นเพื่อให้สำเร็จ นี่ไม่ใช่ระยะเวลาน้อยๆ และต้องอาศัยการมีส่วนร่วมจากหลายคน

หาก WordPress สามารถใช้ GraphQL ได้ และ endpoint สำหรับแก้ไขรูปภาพอิงอยู่บน GraphQL แทน REST ผู้มีส่วนร่วมเหล่านั้นก็สามารถทุ่มเทกับการพัฒนาอื่นๆ แทนได้

WordPress จะดีขึ้นและพัฒนาได้เร็วขึ้นมากไหม ถ้าสามารถใช้ข้อดีที่สุดของแต่ละ API ได้เมื่อสะดวก?

Batch Operations ใน GraphQL

ผมจะแสดงให้เห็นไม่ใช่หนึ่ง แต่หลายวิธีที่ Gato GraphQL รองรับ batch operations

วิธีแรกเป็นวิธีที่ง่ายที่สุด: การเพิ่มหลาย field ไปยัง root ของ query ตัวอย่างเช่น query นี้ล็อกอินผู้ใช้ แล้วเพิ่มความคิดเห็น:

mutation LogUserInAndAddCommentToPost {
  loginUser(
    by: { credentials: { usernameOrEmail: "test", password: "pass" } }
  ) {
    id
    name
  }
  addCommentToCustomPost(
    input: {
      customPostID: 1459
      commentAs: { html: "Adding a comment: bla bla bla" }
    }
  ) {
    id
    content
    date
  }
}

(อนึ่ง นี่คือ GraphiQL client นี่คือบทเรียน เกี่ยวกับการใช้งานมัน)

ตอนนี้ การดำเนินการทั้งสองนี้ถูกใช้กับ object ที่แตกต่างกัน แต่เราต้องการใช้การดำเนินการหลายอย่างกับ object เดียวกัน

มาทำสิ่งนั้นต่อไป: query นี้เพิ่มความคิดเห็นสองรายการในโพสต์เดียวกัน

mutation AddTwoCommentsToPost {
  firstComment: addCommentToCustomPost(
    input: {
      customPostID: 1459
      commentAs: { html: "This is my first response" }
    }
  ) {
    id
    content
    date
  }
  secondComment: addCommentToCustomPost(
    input: {
      customPostID: 1459
      commentAs: { html: "This is my second response" }
    }
  ) {
    id
    content
    date
  }
}

ความคิดเห็นทั้งสองนี้ถูกเพิ่มไปยังโพสต์ที่มีอยู่แล้ว แต่จะเกิดอะไรขึ้นถ้าโพสต์ก็ต้องถูกสร้างขึ้นก่อน?

ในกรณีนั้น query แบบง่ายจะใช้ไม่ได้อีกต่อไป เพราะเราไม่รู้ ID ของโพสต์ที่กำลังจะสร้าง ซึ่งจำเป็นต้องใช้เป็น argument ให้กับการดำเนินการอื่นๆ (สังเกต ? ใน field argument):

mutation CreatePostAndAddTwoCommentsToPost {
  createPost(input: { title: "Some post" }) {
    id  # <= I don't know what this value will be
  }
  addCommentToCustomPost(input: {
    customPostID: ?,
    commentAs: { html: "Blah blah blah" }
  }) {
    id
    content
    date
  }
}

แต่อย่าเพิ่งท้อใจ Gato GraphQL พร้อมช่วยเหลือคุณ มีไม่ใช่หนึ่ง แต่สองโซลูชัน!

GraphQL API ใส่ใจคุณ

วิธีแรกคือการใช้ฟีเจอร์ multiple-query execution

ใน query นี้ เราดำเนินการแรก ส่งออกผลลัพธ์ผ่าน directive @export แล้วแทรกค่านี้เป็น input ให้กับ query ที่สอง:

mutation AddComment {
  addCommentToCustomPost(
    customPostID: 1459
    commentAs: { html: "Some insightful comment" }
  ) {
    id @export(as: "newCommentID")
    content
    date
  }
}
 
mutation AddResponseToComment @depends(on: "AddComment") {
  replyComment(
    parentCommentID: $newCommentID
    commentAs: { html: "Debunking your insightful comment" }
  ) {
    id
    date
    content
    parent {
      id
    }
  }
}

ที่สง่างามกว่านั้น เราสามารถใช้ nested mutations

ใน query นี้ เราดำเนินการแรก และซ้อนการดำเนินการที่สองไว้ภายใน ดังนั้นมันจะถูกใช้กับ object ที่สร้างขึ้นในระหว่างการดำเนินการแรก (และทำซ้ำ โดยซ้อนการดำเนินการที่ 3 และอื่นๆ):

mutation AddCommentAndResponseAndResponse {
  addCommentToCustomPost(
    input: {
      customPostID: 1459
      commentAs: { html: "Some insightful comment" }
    }
  ) {
    id
    content
    date
    reply(input: { commentAs: { html: "Debunking your insightful comment" } }) {
      id
      date
      content
      parent {
        id
      }
      reply(input: { commentAs: { html: "No, it was right!" } }) {
        id
        date
        content
        parent {
          id
        }
      }
    }
  }
}

เพิ่มเติมอีก batch operations สามารถใช้ได้ไม่เพียงกับ entity เดียว แต่กับหลาย entity พร้อมกันใน request เดียวกัน

ใน query นี้ ความคิดเห็นใหม่และการตอบกลับทั้งหมดถูกเพิ่มไปยังหลายโพสต์:

mutation AddCommentAndResponseToManyPosts {
  posts(ids: [1657, 1153, 1499, 1459]) {
    id
    addComment(input: { commentAs: { html: "Some insightful comment" } }) {
      id
      content
      date
      reply(
        input: { commentAs: { html: "Debunking your insightful comment" } }
      ) {
        id
        date
        content
        parent {
          id
        }
      }
    }
  }
}

และ plugin ยังมีเทคนิคอีกหนึ่งอย่างซ่อนอยู่: ด้วยการใช้ฟีเจอร์ embeddable fields เราสามารถปรับแต่งเนื้อหาที่ส่งไปยัง field argument แต่ละตัว โดยใช้ข้อมูลจาก object นั้นเอง!

ใน query นี้ ความคิดเห็นประกอบด้วยข้อมูลจาก object ที่กำลังถูกสร้างขึ้น:

mutation AddCustomCommentAndResponseToManyPosts {
  posts(ids: [1657, 1153, 1499, 1459]) {
    id
    addComment(
      input: {
        commentAs: { html: "The post has ID {{ id }} and title {{ title }}" }
      }
    ) {
      id
      content
      date
      reply(
        input: {
          commentAs: {
            html: "The parent comment was posted on {{ dateStr(format: \"d/m/Y\") }}. Cool, right?"
          }
        }
      ) {
        id
        date
        content
        parent {
          id
        }
      }
    }
  }
}

ดึงสิ่งที่ดีที่สุดจาก REST และ GraphQL เมื่อสะดวก

เมื่อ Full Site Editing ได้รับการพัฒนาและขยายต่อไป WordPress จะพึ่งพา API ของตนมากขึ้นเรื่อยๆ

สำหรับฟีเจอร์ที่มีอยู่แล้ว REST API ทำงานได้ดีมากจนถึงตอนนี้ ไม่จำเป็นต้องสร้างสิ่งที่ไม่ได้เสียหายขึ้นมาใหม่

อย่างไรก็ตาม สำหรับฟีเจอร์ใหม่ที่ยังต้องพัฒนา WordPress จะได้ประโยชน์จากการใช้ REST หรือ GraphQL ขึ้นอยู่กับอะไรที่สะดวกกว่าสำหรับฟีเจอร์นั้นๆ ไหม?

คำตอบขึ้นอยู่กับคุณ...

ความคิดเห็นของคุณคืออะไร?


สมัครรับจดหมายข่าวของเรา

ติดตามการอัปเดตทั้งหมดของ Gato GraphQL