ขยาย Data Connect ด้วยตัวแก้ไขที่กำหนดเอง

การเขียนตัวแก้ไขที่กำหนดเองช่วยให้คุณขยาย Firebase Data Connect เพื่อรองรับแหล่งข้อมูลอื่นๆ นอกเหนือจาก Cloud SQL ได้ จากนั้นคุณจะรวมแหล่งข้อมูลหลายแหล่ง (Cloud SQL และแหล่งข้อมูลที่ตัวแก้ไขที่กำหนดเองของคุณระบุ) ไว้ในคำค้นหาหรือการเปลี่ยนแปลงรายการเดียวได้

แนวคิดของ "แหล่งข้อมูล" นั้นยืดหยุ่น ซึ่งรวมถึง

  • ฐานข้อมูลอื่นๆ นอกเหนือจาก Cloud SQL เช่น Cloud Firestore, MongoDB และ อื่นๆ
  • บริการพื้นที่เก็บข้อมูล เช่น Cloud Storage, AWS S3 และอื่นๆ
  • การผสานรวมที่อิงตาม API เช่น Stripe, SendGrid, Salesforce และอื่นๆ
  • ตรรกะทางธุรกิจที่กำหนดเอง

เมื่อเขียนตัวแก้ไขที่กำหนดเองเพื่อรองรับแหล่งข้อมูลเพิ่มเติมแล้ว Data Connect การค้นหาและการเปลี่ยนแปลงจะรวมแหล่งข้อมูลเหล่านั้นได้หลายวิธี ซึ่งจะให้ประโยชน์ต่างๆ เช่น

  • เลเยอร์การให้สิทธิ์แบบรวมสำหรับแหล่งข้อมูล เช่น อนุญาต การเข้าถึงไฟล์ใน Cloud Storage โดยใช้ข้อมูลที่จัดเก็บไว้ใน Cloud SQL
  • SDK ไคลเอ็นต์ที่ปลอดภัยต่อประเภทสำหรับเว็บ, Android และ iOS
  • การค้นหาที่แสดงข้อมูลจากหลายแหล่ง
  • การเรียกใช้ฟังก์ชันที่จำกัดตามสถานะฐานข้อมูล

ข้อกำหนดเบื้องต้น

หากต้องการเขียนตัวแก้ไขที่กำหนดเอง คุณจะต้องมีสิ่งต่อไปนี้

  • Firebase CLI v15.9.0 ขึ้นไป
  • Firebase Functions SDK v7.1.0 ขึ้นไป

นอกจากนี้ คุณควรคุ้นเคยกับการเขียนฟังก์ชันโดยใช้ Cloud Functions for Firebase ซึ่งเป็นวิธีที่คุณจะใช้ตรรกะของตัวแก้ไขที่กำหนดเอง

ก่อนเริ่มต้น

คุณควรตั้งค่าโปรเจ็กต์ไว้แล้วเพื่อใช้ Data Connect

คุณทำตามคู่มือเริ่มใช้งานฉบับย่อรายการใดรายการหนึ่งเพื่อตั้งค่าได้หากยังไม่ได้ตั้งค่า

เขียนตัวแก้ไขที่กำหนดเอง

การเขียนรีโซลเวอร์ที่กำหนดเองมี 3 ส่วน ได้แก่ ส่วนแรกคือการกำหนดสคีมาสำหรับรีโซลเวอร์ที่กำหนดเอง ส่วนที่ 2 คือการใช้รีโซลเวอร์โดยใช้ Cloud Functions และส่วนสุดท้ายคือการใช้ฟิลด์รีโซลเวอร์ที่กำหนดเองในการค้นหาและการเปลี่ยนแปลง ซึ่งอาจใช้ร่วมกับ Cloud SQL หรือรีโซลเวอร์ที่กำหนดเองอื่นๆ

ทําตามขั้นตอนใน 2-3 ส่วนถัดไปเพื่อดูวิธีทํา สมมติว่าคุณมีข้อมูลโปรไฟล์สาธารณะของผู้ใช้ที่จัดเก็บไว้นอก Cloud SQL เพื่อเป็นตัวอย่างที่สร้างแรงบันดาลใจ ตัวอย่างเหล่านี้ไม่ได้ระบุที่เก็บข้อมูลที่แน่นอน แต่ที่เก็บข้อมูลอาจเป็น Cloud Storage, อินสแตนซ์ MongoDB หรือที่เก็บข้อมูลอื่นๆ

ส่วนต่อไปนี้จะแสดงการติดตั้งใช้งานโครงสร้างของตัวแก้ไขที่กำหนดเองซึ่งสามารถนำข้อมูลโปรไฟล์ภายนอกนั้นมายัง Data Connect ได้

กำหนดสคีมาสำหรับตัวแก้ไขที่กำหนดเอง

  1. ในไดเรกทอรีโปรเจ็กต์ Firebase ให้เรียกใช้คำสั่งต่อไปนี้

    firebase init dataconnect:resolver

    Firebase CLI จะแจ้งให้คุณตั้งชื่อตัวแก้ไขที่กำหนดเอง และถามว่า ต้องการสร้างการติดตั้งใช้งานตัวแก้ไขตัวอย่างใน TypeScript หรือ JavaScript หรือไม่ หากคุณทำตามคำแนะนำนี้ ให้ยอมรับชื่อเริ่มต้นและ สร้างตัวอย่าง TypeScript

    จากนั้นเครื่องมือจะสร้างไฟล์ dataconnect/schema_resolver/schema.gql ที่ว่างเปล่าและเพิ่มการกำหนดค่าตัวแก้ไขใหม่ลงในไฟล์ dataconnect.yaml

  2. อัปเดตไฟล์ schema.gql นี้ด้วยสคีมา GraphQL ที่กำหนดการค้นหา และการเปลี่ยนแปลงที่ตัวแก้ไขที่กำหนดเองจะให้ ตัวอย่างเช่น นี่คือสคีมา สำหรับตัวแก้ไขที่กำหนดเองซึ่งสามารถดึงและอัปเดตโปรไฟล์สาธารณะของผู้ใช้ ที่จัดเก็บไว้ในที่เก็บข้อมูลอื่นที่ไม่ใช่ Cloud SQL

    # dataconnect/schema_resolver/schema.gql
    
    type PublicProfile {
      name: String!
      photoUrl: String!
      bioLine: String!
    }
    
    type Query {
      # This field will be backed by your Cloud Function.
      publicProfile(userId: String!): PublicProfile
    }
    
    type Mutation {
      # This field will be backed by your Cloud Function.
      updatePublicProfile(
        userId: String!, name: String, photoUrl: String, bioLine: String
      ): PublicProfile
    }
    

ใช้ตรรกะของตัวแก้ไขที่กำหนดเอง

จากนั้นใช้ตัวแก้ไขโดยใช้ Cloud Functions เบื้องหลังการทำงาน คุณจะต้อง สร้างเซิร์ฟเวอร์ GraphQL แต่ Cloud Functions มีเมธอดตัวช่วย onGraphRequest ซึ่งจัดการรายละเอียดการดำเนินการดังกล่าว คุณจึงต้อง เขียนตรรกะของตัวแก้ไขที่เข้าถึงแหล่งข้อมูลเท่านั้น

  1. เปิดไฟล์ functions/src/index.ts

    เมื่อคุณเรียกใช้ firebase init dataconnect:resolver ข้างต้น คำสั่งจะสร้างไดเรกทอรีซอร์สโค้ดของ Cloud Functions นี้และเริ่มต้นด้วยโค้ดตัวอย่างใน index.ts

  2. เพิ่มคำจำกัดความต่อไปนี้

    import {
      FirebaseContext,
      onGraphRequest,
    } from "firebase-functions/dataconnect/graphql";
    
    const opts = {
      // Points to the schema you defined earlier, relative to the root of your
      // Firebase project.
      schemaFilePath: "dataconnect/schema_resolver/schema.gql",
      resolvers: {
        query: {
          // This resolver function populates the data for the "publicProfile" field
          // defined in your GraphQL schema located at schemaFilePath.
          publicProfile(
            _parent: unknown,
            args: Record<string, unknown>,
            _contextValue: FirebaseContext,
            _info: unknown
          ) {
            const userId = args.userId;
    
            // Here you would use the user ID to retrieve the user profile from your data
            // store. In this example, we just return a hard-coded value.
    
            return {
              name: "Ulysses von Userberg",
              photoUrl: "https://example.com/profiles/12345/photo.jpg",
              bioLine: "Just a guy on a mountain. Ski fanatic.",
            };
          },
        },
        mutation: {
          // This resolver function updates data for the "updatePublicProfile" field
          // defined in your GraphQL schema located at schemaFilePath.
          updatePublicProfile(
            _parent: unknown,
            args: Record<string, unknown>,
            _contextValue: FirebaseContext,
            _info: unknown
          ) {
            const { userId, name, photoUrl, bioLine } = args;
    
            // Here you would update in your datastore the user's profile using the
            // arguments that were passed. In this example, we just return the profile
            // as though the operation had been successful.
    
            return { name, photoUrl, bioLine };
          },
        },
      },
    };
    
    export const resolver = onGraphRequest(opts);
    

การติดตั้งใช้งานโครงร่างเหล่านี้แสดงรูปร่างทั่วไปที่ฟังก์ชันตัวแก้ไข ต้องใช้ หากต้องการสร้างตัวแก้ไขที่กำหนดเองซึ่งทำงานได้อย่างเต็มรูปแบบ คุณจะต้องกรอกข้อมูลในส่วนที่แสดงความคิดเห็นด้วยโค้ดที่อ่านและเขียนไปยังแหล่งข้อมูล

ใช้ตัวแก้ไขที่กำหนดเองในการค้นหาและการเปลี่ยนแปลง

เมื่อกำหนดสคีมาของตัวแก้ไขที่กำหนดเองและ ใช้ตรรกะที่รองรับแล้ว คุณจะใช้ตัวแก้ไขที่กำหนดเองใน Data Connect การค้นหาและการเปลี่ยนแปลงได้ ในภายหลัง คุณจะใช้การดำเนินการเหล่านี้ เพื่อสร้าง SDK ไคลเอ็นต์ที่กำหนดเองโดยอัตโนมัติ ซึ่งคุณสามารถใช้เพื่อเข้าถึงข้อมูลทั้งหมด ได้ ไม่ว่าจะได้รับการสนับสนุนจาก Cloud SQL, ตัวแก้ไขที่กำหนดเอง หรือทั้ง 2 อย่างรวมกัน

  1. ใน dataconnect/example/queries.gql ให้เพิ่มคำจำกัดความต่อไปนี้

    query GetPublicProfile($id: String!)
        @auth(level: PUBLIC, insecureReason: "Anyone can see a public profile.") {
      publicProfile(userId: $id) {
        name
        photoUrl
        bioLine
      }
    }
    

    การค้นหานี้จะดึงโปรไฟล์สาธารณะของผู้ใช้โดยใช้ตัวแก้ไขที่กำหนดเอง

  2. ใน dataconnect/example/mutations.gql ให้เพิ่มคำจำกัดความต่อไปนี้

    mutation SetPublicProfile(
      $id: String!, $name: String, $photoUrl: String, $bioLine: String
    ) @auth(expr: "vars.id == auth.uid") {
      updatePublicProfile(userId: $id, name: $name, photoUrl: $photoUrl, bioLine: $bioLine) {
        name
        photoUrl
        bioLine
      }
    }
    

    การเปลี่ยนแปลงนี้จะเขียนชุดข้อมูลโปรไฟล์ใหม่ลงใน Datastore โดยใช้ ตัวแก้ไขที่กำหนดเองอีกครั้ง โปรดทราบว่าสคีมาใช้ Data Connect's @auth directive เพื่อให้มั่นใจว่าผู้ใช้จะ อัปเดตได้เฉพาะโปรไฟล์ของตนเอง เนื่องจากคุณเข้าถึง Datastore ผ่าน Data Connect คุณจึงใช้ประโยชน์จากฟีเจอร์ของ Data Connect เช่น ฟีเจอร์นี้ได้โดยอัตโนมัติ

ในตัวอย่างข้างต้น คุณได้กำหนดData Connectการดำเนินการที่เข้าถึงข้อมูล จากที่เก็บข้อมูลโดยใช้ตัวแก้ไขที่กำหนดเอง อย่างไรก็ตาม คุณไม่ได้ถูกจำกัด ในการดำเนินการเพื่อเข้าถึงข้อมูลจาก Cloud SQL หรือจากแหล่งข้อมูลที่กำหนดเอง เพียงแหล่งเดียว ดูตัวอย่างเพิ่มเติมในส่วนตัวอย่างสำหรับกรณีการใช้งานขั้นสูง ที่รวมข้อมูลจากหลายแหล่ง

ก่อนอื่น ให้ไปที่ส่วนถัดไปเพื่อดูตัวแก้ไขแบบกำหนดเองของคุณใน การทำงาน

ติดตั้งใช้งานตัวแก้ไขและตัวดำเนินการที่กำหนดเอง

เช่นเดียวกับการเปลี่ยนแปลงData Connectสคีมา คุณต้อง ติดตั้งใช้งานเพื่อให้มีผล ก่อนที่จะดำเนินการดังกล่าว ให้ติดตั้งใช้งานตรรกะของตัวแก้ไขที่กำหนดเองที่คุณใช้ Cloud Functions สร้างขึ้นก่อน

firebase deploy --only functions

ตอนนี้คุณสามารถติดตั้งใช้งานสคีมาและการดำเนินการที่อัปเดตแล้วได้โดยทำดังนี้

firebase deploy --only dataconnect

หลังจากทำการเปลี่ยนแปลงสคีมา Data Connect แล้ว คุณต้อง สร้าง SDK ไคลเอ็นต์ใหม่ด้วย

firebase dataconnect:sdk:generate

ตัวอย่าง

ตัวอย่างเหล่านี้แสดงวิธีใช้กรณีการใช้งานขั้นสูงบางอย่าง และวิธี หลีกเลี่ยงข้อผิดพลาดที่พบบ่อย

การให้สิทธิ์เข้าถึงตัวแก้ไขที่กำหนดเองโดยใช้ข้อมูลจาก Cloud SQL

ข้อดีอย่างหนึ่งของการผสานรวมแหล่งข้อมูลเข้ากับ Data Connect โดยใช้ตัวแก้ไขที่กำหนดเองคือคุณสามารถเขียนการดำเนินการที่รวมแหล่งข้อมูล ได้

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

หากต้องการใช้ฟีเจอร์การกระตุ้น ให้สร้างตัวแก้ไขที่กำหนดเองด้วยสคีมาดังต่อไปนี้

# A GraphQL server must define a root query type per the spec.
type Query {
  unused: String
}

type Mutation {
  sendEmail(id: String!, content: String): Boolean
}

คำจำกัดความนี้ได้รับการสนับสนุนโดย Cloud Function เช่น ฟังก์ชันต่อไปนี้

import {
  FirebaseContext,
  onGraphRequest,
} from "firebase-functions/dataconnect/graphql";

const opts = {
  schemaFilePath: "dataconnect/schema_resolver/schema.gql",
  resolvers: {
    mutation: {
      sendEmail(
        _parent: unknown,
        args: Record<string, unknown>,
        _contextValue: FirebaseContext,
        _info: unknown
      ) {
        const { id, content } = args;

        // Look up the friend's email address and call the cloud service of your
        // choice to send the friend an email with the given content.

       return true;
      },
    },
  },
};

export const resolver = onGraphRequest(opts);

เนื่องจากการส่งอีเมลมีค่าใช้จ่ายทั้งสำหรับคุณและอาจเป็นช่องทางที่ทำให้เกิดการละเมิด คุณจึงควรตรวจสอบว่าผู้รับที่ต้องการอยู่ในรายชื่อเพื่อนของผู้ใช้แล้ว ก่อนที่จะใช้ sendEmail ตัวแก้ไขที่กำหนดเอง

สมมติว่าในแอปของคุณ ระบบจัดเก็บข้อมูลรายชื่อเพื่อนไว้ใน Cloud SQL

type User @table {
  id: String! @default(expr: "auth.uid")
  acceptNudges: Boolean! @default(value: false)
}

type UserFriend @table(key: ["user", "friend"]) {
  user: User!
  friend: User!
}

คุณเขียนการเปลี่ยนแปลงที่ค้นหา Cloud SQL ก่อนเพื่อให้แน่ใจว่าผู้ส่งอยู่ในรายชื่อเพื่อนของผู้รับก่อนที่จะใช้ตัวแก้ไขที่กำหนดเองเพื่อส่งอีเมลได้โดยทำดังนี้

# Send a "nudge" to a friend as a reminder. This will only let the user send a
# nudge if $friendId is in the user's friends list.
mutation SendNudge($friendId: String!) @auth(level: USER_EMAIL_VERIFIED) {
  # Step 1: Query and check
  query @redact {
    userFriend(
      key: {userId_expr: "auth.uid", friendId: $friendId}
    # This checks that $friendId is in the user's friends list.
    ) @check(expr: "this != null", message: "You must be friends to nudge") {
      friend {
        # This checks that the friend is accepting nudges.
        acceptNudges @check(expr: "this == true", message: "Not accepting nudges")
      }
    }
  }
  # Step 2: Act
  sendEmail(id: $friendId, content: "You've been nudged!")
}

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

การตรวจสอบการดำเนินการตามลำดับโดยใช้การเปลี่ยนแปลง

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

ความพยายามครั้งแรกในการบรรลุเป้าหมายนี้อาจมีลักษณะดังนี้

# This won't work as expected.
query BrokenTranscribeVideo($videoId: UUID!) @auth(level: USER_EMAIL_VERIFIED) {
  # Step 1: Check quota using SQL.
  # Verify the user owns the video and has "pro" status or credits.
  checkQuota: query @redact {
    video(id: $videoId)
    {
      user @check(expr: "this.id == auth.uid && this.hasCredits == true", message: "Unauthorized access") {
        id
        hasCredits
      }
    }
  }

  # Step 2: Trigger expensive compute
  # Only triggers if Step 1 succeeds? No! This won't work because query field
  # execution order is not guaranteed.
  triggerTranscription: query {
    # For example, might call Vertex AI or Transcoder API.
    startVideoTranscription(videoId: $videoId)
  }
}

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

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

# By using a mutation, we guarantee the SQL check happens FIRST.
mutation TranscribeVideo($videoId: UUID!) @auth(level: USER_EMAIL_VERIFIED) {
  # Step 1: Check quota using SQL.
  # Verify the user owns the video and has "pro" status or credits.
  checkQuota: query @redact {
    video(id: $videoId)
    {
      user @check(expr: "this.id == auth.uid && this.hasCredits == true", message: "Unauthorized access") {
        id
        hasCredits
      }
    }
  }

  # Step 2: Trigger expensive compute
  # This Cloud Function will ONLY trigger if Step 1 succeeds.
  triggerTranscription: query {
    # For example, might call Vertex AI or Transcoder API.
    startVideoTranscription(videoId: $videoId)
  }
}

ข้อจำกัด

ฟีเจอร์ตัวแก้ไขที่กำหนดเองเปิดตัวเป็นเวอร์ชันตัวอย่างแบบสาธารณะทดลอง โปรดทราบ ข้อจำกัดปัจจุบันต่อไปนี้

ไม่มีนิพจน์ CEL ในอาร์กิวเมนต์ของตัวแก้ไขที่กำหนดเอง

คุณไม่สามารถใช้นิพจน์ CEL แบบไดนามิกในอาร์กิวเมนต์ของตัวแก้ไขที่กำหนดเองได้ ตัวอย่างเช่น คุณจะทำสิ่งต่อไปนี้ไม่ได้

mutation UpdateMyProfile($newName: String!) @auth(level: USER) {
  updateMongoDocument(
    collection: "profiles"
    # This isn't supported:
    id_expr: "auth.uid"
    update: { name: $newName }
  )
}

แต่ให้ส่งตัวแปรมาตรฐาน (เช่น $authUid) และตรวจสอบตัวแปรเหล่านั้นที่ระดับการดำเนินการโดยใช้คำสั่ง @auth(expr: ...) ที่ประเมินอย่างปลอดภัยแทน

mutation UpdateMyProfile(
  $newName: String!, $authUid: String!
) @auth(expr: "vars.authUid == auth.uid") {
  updateMongoDocument(
    collection: "profiles"
    id: $authUid
    update: { name: $newName }
  )
}

อีกวิธีแก้ปัญหาคือการย้ายตรรกะทั้งหมดไปยังตัวแก้ไขที่กำหนดเองและ ดำเนินการข้อมูลทั้งหมดจาก Cloud Functions

เช่น ลองดูตัวอย่างนี้ ซึ่งปัจจุบันยังใช้ไม่ได้

mutation BrokenForwardToEmail($chatMessageId: UUID!) @auth(level: USER_EMAIL_VERIFIED) {
  query {
    chatMessage(id: $chatMessageId) {
      content
    }
  }
  sendEmail(
    title: "Forwarded Chat Message"
    to_expr: "auth.token.email" # Not supported.
    content_expr: "response.query.chatMessage.content" # Not supported.
  )
}

แต่ให้ย้ายทั้งการค้นหา Cloud SQL และการเรียกใช้บริการอีเมลไปไว้ใน ฟิลด์การเปลี่ยนแปลงเดียวที่ทำงานโดยฟังก์ชันแทน

mutation ForwardToEmail($chatMessageId: UUID!) @auth(level: USER_EMAIL_VERIFIED) {
  forwardChatToEmail(
    chatMessageId: $chatMessageId
  )
}

สร้าง Admin SDK สำหรับฐานข้อมูลและใช้ในฟังก์ชันเพื่อทำการค้นหา Cloud SQL ดังนี้

const opts = {
 schemaFilePath: "dataconnect/schema_resolver/schema.gql",
 resolvers: {
   query: {
     async forwardToEmail(
       _parent: unknown,
       args: Record<string, unknown>,
       _contextValue: FirebaseContext,
       _info: unknown
     ) {
       const chatMessageId = args.chatMessageId as string;

       let decodedToken;
       try {
         decodedToken = await getAuth().verifyIdToken(_contextValue.auth.token ?? "");
       } catch (error) {
         return false;
       }

       const email = decodedToken.email;
       if (!email) {
         return false;
       }

       const response = await getChatMessage({chatMessageId});
       const messageContent = response.data.chatMessage?.content;

       // Here you call the cloud service of your choice to send the email with
       // the message content.

       return true;
     }
   },
 },
};
export const resolver = onGraphRequest(opts);

ไม่มีประเภทออบเจ็กต์อินพุตในพารามิเตอร์ของตัวแก้ไขที่กำหนดเอง

ตัวแก้ไขที่กำหนดเองไม่ยอมรับประเภทอินพุต GraphQL ที่ซับซ้อน พารามิเตอร์ต้องเป็นประเภทสเกลาร์พื้นฐาน (String, Int, Date, Any ฯลฯ) และ Enum

input PublicProfileInput {
  name: String!
  photoUrl: String!
  bioLine: String!
}

type Mutation {
  # Not supported:
  updatePublicProfile(userId: String!, profile: PublicProfileInput): PublicProfile

  # OK:
  updatePublicProfile(userId: String!, name: String, photoUrl: String, bioLine: String): PublicProfile
}

ตัวแก้ไขที่กำหนดเองต้องไม่มาก่อนการดำเนินการ SQL

ในการเปลี่ยนแปลง การวางตัวแก้ไขที่กำหนดเองก่อนการดำเนินการ SQL มาตรฐาน จะทำให้เกิดข้อผิดพลาด การดำเนินการที่อิงตาม SQL ทั้งหมดต้องปรากฏก่อนการเรียกใช้ตัวแก้ไขที่กำหนดเอง

ไม่มีธุรกรรม (@transaction)

ไม่สามารถรวมตัวแก้ไขที่กำหนดเองไว้ในบล็อก @transaction ที่มีการดำเนินการ SQL มาตรฐาน หาก Cloud Function ที่สนับสนุนตัวแก้ไขล้มเหลวหลังจากที่การแทรก SQL สำเร็จ ฐานข้อมูลจะไม่ย้อนกลับโดยอัตโนมัติ

หากต้องการให้การทำธุรกรรมระหว่าง SQL กับแหล่งข้อมูลอื่นปลอดภัย ให้ย้ายตรรกะการดำเนินการ SQL ไปไว้ใน Cloud Function และจัดการการตรวจสอบและการย้อนกลับโดยใช้ Admin SDK หรือการเชื่อมต่อ SQL โดยตรง