การเขียนตัวแก้ไขที่กำหนดเองช่วยให้คุณขยาย 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 ได้
กำหนดสคีมาสำหรับตัวแก้ไขที่กำหนดเอง
ในไดเรกทอรีโปรเจ็กต์ Firebase ให้เรียกใช้คำสั่งต่อไปนี้
firebase init dataconnect:resolverFirebase CLI จะแจ้งให้คุณตั้งชื่อตัวแก้ไขที่กำหนดเอง และถามว่า ต้องการสร้างการติดตั้งใช้งานตัวแก้ไขตัวอย่างใน TypeScript หรือ JavaScript หรือไม่ หากคุณทำตามคำแนะนำนี้ ให้ยอมรับชื่อเริ่มต้นและ สร้างตัวอย่าง TypeScript
จากนั้นเครื่องมือจะสร้างไฟล์
dataconnect/schema_resolver/schema.gqlที่ว่างเปล่าและเพิ่มการกำหนดค่าตัวแก้ไขใหม่ลงในไฟล์dataconnect.yamlอัปเดตไฟล์
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 ซึ่งจัดการรายละเอียดการดำเนินการดังกล่าว คุณจึงต้อง
เขียนตรรกะของตัวแก้ไขที่เข้าถึงแหล่งข้อมูลเท่านั้น
เปิดไฟล์
functions/src/index.tsเมื่อคุณเรียกใช้
firebase init dataconnect:resolverข้างต้น คำสั่งจะสร้างไดเรกทอรีซอร์สโค้ดของ Cloud Functions นี้และเริ่มต้นด้วยโค้ดตัวอย่างในindex.tsเพิ่มคำจำกัดความต่อไปนี้
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 อย่างรวมกัน
ใน
dataconnect/example/queries.gqlให้เพิ่มคำจำกัดความต่อไปนี้query GetPublicProfile($id: String!) @auth(level: PUBLIC, insecureReason: "Anyone can see a public profile.") { publicProfile(userId: $id) { name photoUrl bioLine } }การค้นหานี้จะดึงโปรไฟล์สาธารณะของผู้ใช้โดยใช้ตัวแก้ไขที่กำหนดเอง
ใน
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
@authdirective เพื่อให้มั่นใจว่าผู้ใช้จะ อัปเดตได้เฉพาะโปรไฟล์ของตนเอง เนื่องจากคุณเข้าถึง 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 โดยตรง