การโต้ตอบกับ GraphQL API
การโต้ตอบกับ GraphQL APIการจัดการ Mutation Payload

การจัดการ Mutation Payload

ฟิลด์ mutation สามารถกำหนดค่าให้คืนค่าเป็นประเภทเอนทิตีใดประเภทหนึ่งใน 2 ประเภทต่อไปนี้

  • ประเภท payload object
  • คืนค่าเอนทิตีที่ถูก mutate โดยตรง

ประเภท Payload Object

ประเภท payload object ประกอบด้วยข้อมูลทั้งหมดที่เกี่ยวข้องกับ mutation ได้แก่

  • สถานะของ mutation (สำเร็จหรือล้มเหลว)
  • ข้อผิดพลาด (ถ้ามี) โดยใช้ประเภท GraphQL ที่แตกต่างกัน หรือ
  • เอนทิตีที่ถูก mutate สำเร็จ

ตัวอย่างเช่น mutation updatePost คืนค่าออบเจ็กต์ประเภท PostUpdateMutationPayload และเราจำเป็นต้องคิวรีฟิลด์ post เพื่อดึงข้อมูลเอนทิตีโพสต์ที่อัปเดต

mutation UpdatePost {
  updatePost(input: {
    id: 1724,
    title: "New title",
    status: publish
  }) {
    # This is the status of the mutation: SUCCESS or FAILURE
    status
    errors {
      __typename
      ...on ErrorPayload {
        message
      }
    }
    post {
      id
      title
      # This is the status of the post: publish, pending, trash, etc
      status
    }
  }
}

payload object ช่วยให้เราสามารถแสดงข้อผิดพลาดได้ดียิ่งขึ้น แม้แต่การมีประเภท GraphQL ที่ไม่ซ้ำกันสำหรับข้อผิดพลาดแต่ละประเภท ซึ่งช่วยให้เราสามารถแสดงการตอบสนองที่แตกต่างกันสำหรับข้อผิดพลาดที่แตกต่างกันในแอปพลิเคชัน จึงปรับปรุงประสบการณ์ของผู้ใช้

ในตัวอย่างข้างต้น หากการดำเนินการสำเร็จ เราจะได้รับ

{
  "data": {
    "updatePost": {
      "status": "SUCCESS",
      "errors": null,
      "post": {
        "id": 1724,
        "title": "Some title",
        "status": "publish"
      }
    }
  }
}

หากผู้ใช้ไม่ได้เข้าสู่ระบบ เราจะได้รับ

{
  "data": {
    "updatePost": {
      "status": "FAILURE",
      "errors": [
        {
          "__typename": "UserIsNotLoggedInErrorPayload",
          "message": "You must be logged in to create or update custom posts"
        }
      ],
      "post": null
    }
  }
}

หากผู้ใช้ไม่มีสิทธิ์ในการแก้ไขโพสต์ เราจะได้รับ

{
  "data": {
    "updatePost": {
      "status": "FAILURE",
      "errors": [
        {
          "__typename": "LoggedInUserHasNoEditingCustomPostCapabilityErrorPayload",
          "message": "Your user doesn't have permission for editing custom posts."
        }
      ],
      "post": null
    }
  }
}

ในโหมดนี้ GraphQL schema จะมีประเภท MutationPayload, MutationErrorPayloadUnion และ ErrorPayload เพิ่มเติมจำนวนมาก ทำให้ schema มีขนาดใหญ่ขึ้น

GraphQL schema with payload object types for mutations

คิวรี Mutation Payload Objects

ทุก mutation ใน schema มีฟิลด์ที่ตรงกันสำหรับคิวรี payload objects ที่เพิ่งสร้างขึ้น โดยมีชื่อว่า {mutationName}MutationPayloadObjects

ฟิลด์เหล่านี้ได้แก่

  • addCommentToCustomPostMutationPayloadObjects (สำหรับ addCommentToCustomPost)
  • createCustomPostMutationPayloadObjects (สำหรับ createCustomPost)
  • createMediaItemMutationPayloadObjects (สำหรับ createMediaItem)
  • createPageMutationPayloadObjects (สำหรับ createPage)
  • createPostMutationPayloadObjects (สำหรับ createPost)
  • removeFeaturedImageFromCustomPostMutationPayloadObjects (สำหรับ removeFeaturedImageFromCustomPost)
  • replyCommentMutationPayloadObjects (สำหรับ replyComment)
  • setCategoriesOnPostMutationPayloadObjects (สำหรับ setCategoriesOnPost)
  • setFeaturedImageOnCustomPostMutationPayloadObjects (สำหรับ setFeaturedImageOnCustomPost)
  • setTagsOnPostMutationPayloadObjects (สำหรับ setTagsOnPost)
  • updateCustomPostMutationPayloadObjects (สำหรับ updateCustomPost)
  • updatePageMutationPayloadObjects (สำหรับ updatePage)
  • updatePostMutationPayloadObjects (สำหรับ updatePost)

ฟิลด์เหล่านี้ช่วยให้เราสามารถดึงผลลัพธ์ของ mutations ที่ดำเนินการโดยใช้ @applyField ขณะวนซ้ำรายการในอาร์เรย์

ตัวอย่างเช่น คิวรีต่อไปนี้ทำการ duplicate โพสต์จำนวนมาก

query GetPostsAndExportData
{
  postsToDuplicate: posts {
    title
    rawContent
    excerpt
 
    # Already create (and export) the inputs for the mutation
    postInput: _echo(value: {
      title: $__title
      contentAs: {
        html: $__rawContent
      },
      excerpt: $__excerpt
    })
      @export(as: "postInput", type: LIST)
      @remove
  }
}
 
mutation CreatePosts
  @depends(on: "GetPostsAndExportData")
{
  createdPostMutationPayloadObjectIDs: _echo(value: $postInput)
    @underEachArrayItem(
      passValueOnwardsAs: "input"
    )
      @applyField(
        name: "createPost"
        arguments: {
          input: $input
        },
        setResultInResponse: true
      )
    @export(as: "createdPostMutationPayloadObjectIDs")
}
 
query DuplicatePosts
  @depends(on: "CreatePosts")
{
  createdPostMutationObjectPayloads: createPostMutationPayloadObjects(input: {
    ids: $createdPostMutationPayloadObjectIDs
  }) {
    status
    errors {
      __typename
      ...on ErrorPayload {
        message
      }
    }
    post {
      id
      title
      rawContent
      excerpt
    }
  }
}

โดยค่าเริ่มต้น ฟิลด์เหล่านี้จะไม่ถูกเพิ่มใน GraphQL schema เพื่อเพิ่มฟิลด์เหล่านี้ เราต้องเลือกตัวเลือก "Use payload types for mutations, and add fields to query those payload objects"

เอนทิตีที่ถูก Mutate

Mutation จะคืนค่าเอนทิตีที่ถูก mutate โดยตรงหากสำเร็จ หรือ null หากล้มเหลว และข้อความแสดงข้อผิดพลาดจะแสดงในรายการ errors ระดับบนสุดของ JSON response

ตัวอย่างเช่น mutation updatePost จะคืนค่าออบเจ็กต์ประเภท Post

mutation UpdatePost {
  updatePost(input: {
    id: 1724,
    title: "New title",
    status: publish
  }) {
    id
    title
    status
  }
}

หากการดำเนินการสำเร็จ เราจะได้รับ

{
  "data": {
    "updatePost": {
      "id": 1724,
      "title": "Some title",
      "status": "publish"
    }
  }
}

ในกรณีที่เกิดข้อผิดพลาด ข้อผิดพลาดเหล่านั้นจะปรากฏใน errors entry ของ response ตัวอย่างเช่น หากผู้ใช้ไม่ได้เข้าสู่ระบบ เราจะได้รับ

{
    "errors": [
      {
        "message": "You must be logged in to create or update custom posts'",
        "locations": [
          {
            "line": 2,
            "column": 3
          }
        ]
      }
  ],
  "data": {
    "updatePost": null
  }
}

เราต้องสังเกตว่า ในผลลัพธ์นี้ errors entry ระดับบนสุดจะมีไม่เพียงแต่ข้อผิดพลาดทางไวยากรณ์ การตรวจสอบ schema และข้อผิดพลาดเชิงตรรกะ (เช่น ไม่ระบุชื่อ argument ของฟิลด์ ร้องขอฟิลด์ที่ไม่มีอยู่ หรือเรียก _sendHTTPRequest ขณะที่เครือข่ายล่ม) แต่ยังรวมถึงข้อผิดพลาดด้าน "content validation" ด้วย (เช่น "คุณไม่มีสิทธิ์แก้ไขโพสต์นี้")

เนื่องจากไม่มีการเพิ่มประเภทพิเศษใดๆ GraphQL schema จะดูกระชับยิ่งขึ้น

GraphQL schema without payload object types for mutations

การจัดการประเภท Payload Object สำหรับ Mutations

มาดูวิธีจัดการตัวเลือกแรก คือประเภท payload object กัน

Mutations ใน schema คืนค่า payload object บางอย่าง ซึ่งให้ข้อมูลข้อผิดพลาดที่เกิดจาก mutation หรือออบเจ็กต์ที่แก้ไขแล้วหากสำเร็จ (คุณสมบัติทั้ง 2 นี้มักจะ exclusive กัน: errors หรือ object อย่างใดอย่างหนึ่งจะมีค่า และอีกอย่างจะเป็น null)

ข้อผิดพลาดถูกให้ผ่านประเภท "ErrorPayloadUnion" ที่มีข้อผิดพลาดที่เป็นไปได้ทั้งหมดสำหรับ mutation นั้น ข้อผิดพลาดที่เป็นไปได้แต่ละรายการเป็นประเภท "ErrorPayload" บางประเภทที่ implements interface ErrorPayload

ตัวอย่างเช่น operation updatePost คืนค่า PostUpdateMutationPayload ซึ่งมีฟิลด์ต่อไปนี้

  • status: บ่งบอกว่า operation สำเร็จหรือไม่ โดยมีค่า SUCCESS หรือ FAILURE
  • post และ postID: ออบเจ็กต์โพสต์ที่อัปเดตและ ID ของมัน หากการอัปเดตสำเร็จ
  • errors: รายการของ CustomPostUpdateMutationErrorPayloadUnion หากการอัปเดตล้มเหลว

ประเภท union CustomPostUpdateMutationErrorPayloadUnion มีรายการข้อผิดพลาดที่เป็นไปได้ทั้งหมดที่อาจเกิดขึ้นเมื่อแก้ไข custom post

  • CustomPostDoesNotExistErrorPayload
  • GenericErrorPayload
  • LoggedInUserHasNoEditingCustomPostCapabilityErrorPayload
  • LoggedInUserHasNoPermissionToEditCustomPostErrorPayload
  • LoggedInUserHasNoPublishingCustomPostCapabilityErrorPayload
  • UserIsNotLoggedInErrorPayload

ประเภทข้อผิดพลาด GenericErrorPayload อยู่ในประเภท "ErrorPayloadUnion" ทั้งหมด ใช้เมื่อไม่สามารถระบุสาเหตุที่แน่ชัดของข้อผิดพลาดได้ เช่น เมื่อ wp_update_post สร้าง WP_Error เพียงอย่างเดียว ประเภทนี้มีฟิลด์เพิ่มเติม 2 ฟิลด์คือ code และ data

จากนั้น เพื่อดำเนินการ updatePost mutation เราสามารถรัน

mutation UpdatePost(
  $postId: ID!
  $title: String!
) {
  updatePost(
    input: {
      id: $postId,
      title: $title,
    }
  ) {
    status
    errors {
      __typename
      ...on ErrorPayload {
        message
      }
      ...on GenericErrorPayload {
        code
      }
    }
    post {
      id
      title
    }
  }
}

หากการดำเนินการสำเร็จ เราจะได้รับ

{
  "data": {
    "updatePost": {
      "status": "SUCCESS",
      "errors": null,
      "post": {
        "id": 1724,
        "title": "This incredible title"
      }
    }
  }
}

หากผู้ใช้ไม่ได้เข้าสู่ระบบ เราจะได้รับ

{
  "data": {
    "updatePost": {
      "status": "FAILURE",
      "errors": [
        {
          "__typename": "UserIsNotLoggedInErrorPayload",
          "message": "You must be logged in to create or update custom posts"
        }
      ],
      "post": null
    }
  }
}

หากผู้ใช้ไม่มีสิทธิ์ในการแก้ไขโพสต์ เราจะได้รับ

{
  "data": {
    "updatePost": {
      "status": "FAILURE",
      "errors": [
        {
          "__typename": "LoggedInUserHasNoEditingCustomPostCapabilityErrorPayload",
          "message": "Your user doesn't have permission for editing custom posts."
        }
      ],
      "post": null
    }
  }
}