Menerapkan mutasi Data Connect

Firebase Data Connect memungkinkan Anda membuat konektor untuk instance PostgreSQL yang dikelola dengan Google Cloud SQL. Konektor ini adalah kombinasi kueri dan mutasi untuk menggunakan data dari skema Anda.

Panduan memulai memperkenalkan skema aplikasi ulasan film untuk PostgreSQL.

Panduan tersebut juga memperkenalkan operasi administratif yang dapat di-deploy dan ad hoc, termasuk mutasi.

  • Mutasi yang dapat di-deploy adalah mutasi yang Anda terapkan untuk dipanggil dari aplikasi klien di konektor, dengan endpoint API yang Anda tentukan. Data Connect mengintegrasikan autentikasi dan otorisasi ke dalam mutasi ini, serta membuat SDK klien berdasarkan API Anda.
  • Mutasi administratif ad hoc dijalankan dari lingkungan yang memiliki hak istimewa untuk mengisi dan mengelola tabel. Anda dapat membuat dan mengeksekusinya di konsol Firebase, dari lingkungan dengan hak istimewa menggunakan Firebase Admin SDK, dan di lingkungan pengembangan lokal menggunakan ekstensi VS Code Data Connect kami.

Panduan ini membahas lebih dalam mutasi yang dapat di-deploy.

Fitur mutasi Data Connect

Data Connect memungkinkan Anda melakukan mutasi dasar dengan semua cara yang Anda harapkan mengingat database PostgreSQL:

  • Melakukan operasi CRUD
  • Mengelola operasi multi-langkah dengan transaksi

Namun, dengan ekstensi Data Connect ke GraphQL, Anda dapat menerapkan mutasi lanjutan untuk aplikasi yang lebih cepat dan efisien:

  • Gunakan skalar kunci yang ditampilkan oleh banyak operasi untuk menyederhanakan operasi berulang pada kumpulan data
  • Gunakan nilai server untuk mengisi data dengan operasi yang disediakan oleh server
  • Lakukan kueri selama operasi mutasi multi-langkah untuk mencari data, sehingga menghemat baris kode dan perjalanan pulang pergi ke server.

Menggunakan kolom yang dibuat untuk menerapkan mutasi

Operasi Data Connect Anda akan memperluas sekumpulan kolom Data Connect yang dibuat secara otomatis berdasarkan jenis dan hubungan jenis dalam skema Anda. Kolom ini dibuat oleh alat lokal setiap kali Anda mengedit skema.

Anda dapat menggunakan kolom yang dibuat untuk menerapkan mutasi, mulai dari membuat, memperbarui, dan menghapus masing-masing data dalam satu tabel, hingga pembaruan multi-tabel yang lebih kompleks.

Asumsikan skema Anda berisi jenis Movie dan jenis Actor terkait. Data Connect menghasilkan kolom movie_insert, movie_update, movie_delete, dan lainnya.

Mutasi dengan kolom
movie_insert

Kolom movie_insert mewakili mutasi untuk membuat satu data dalam tabel Movie.

Gunakan kolom ini untuk membuat satu film.

mutation CreateMovie($data: Movie_Data!) {
  movie_insert(data: $data) { key }
}

Mutasi dengan kolom
movie_update

Kolom movie_update mewakili mutasi untuk memperbarui satu data dalam tabel Movie.

Gunakan kolom ini untuk memperbarui satu film berdasarkan kuncinya.

mutation UpdateMovie($myKey: Movie_Key!, $data: Movie_Data!) {
  movie_update(key: $myKey, data: $data) { key }
}

Mutasi dengan kolom
movie_delete

Kolom movie_delete mewakili mutasi untuk menghapus satu data dalam tabel Movie.

Gunakan kolom ini untuk menghapus satu film berdasarkan kuncinya.

  mutation DeleteMovie($myKey: Movie_Key!) {
    movie_delete(key: $myKey) { key }
  }

Elemen penting mutasi

Mutasi Data Connect adalah mutasi GraphQL dengan ekstensi Data Connect. Sama seperti mutasi GraphQL biasa, Anda dapat menentukan nama operasi dan daftar variabel GraphQL.

Data Connect memperluas kueri GraphQL dengan direktif yang disesuaikan seperti @auth dan @transaction.

Jadi mutasi berikut memiliki:

  • Definisi jenis mutation
  • Nama operasi SignUp (mutasi)
  • Argumen operasi $username variabel tunggal
  • Satu arahan, @auth
  • Satu kolom user_insert.
mutation SignUp($username: String!) @auth(level: USER) {
  user_insert(data: {
    id_expr: "auth.uid"
    username: $username
  })
}

Setiap argumen mutasi memerlukan deklarasi jenis, bawaan seperti String, atau jenis yang ditentukan skema kustom seperti Movie.

Menulis mutasi dasar

Anda dapat mulai menulis mutasi untuk membuat, memperbarui, dan menghapus setiap catatan dari database Anda.

Buat

Mari kita lakukan pembuatan dasar.

# Create a movie based on user input
mutation CreateMovie($title: String!, $releaseYear: Int!, $genre: String!, $rating: Int!) {
  movie_insert(data: {
    title: $title
    releaseYear: $releaseYear
    genre: $genre
    rating: $rating
  })
}

# Create a movie with default values
mutation CreateMovie2 {
  movie_insert(data: {
    title: "Sherlock Holmes"
    releaseYear: 2009
    genre: "Mystery"
    rating: 5
  })
}

Atau upsert.

# Movie upsert using combination of variables and literals
mutation UpsertMovie($title: String!) {
  movie_upsert(data: {
    title: $title
    releaseYear: 2009
    genre: "Mystery"
    rating: 5
    genre: "Mystery/Thriller"
  })
}

Melakukan update

Berikut info terbarunya. Produser dan sutradara tentu berharap rating rata-rata tersebut sesuai tren.

Kolom movie_update berisi argumen id yang diharapkan untuk mengidentifikasi record dan kolom data yang dapat Anda gunakan untuk menetapkan nilai dalam pembaruan ini.

mutation UpdateMovie(
  $id: UUID!,
  $genre: String!,
  $rating: Int!,
  $description: String!
) {
  movie_update(id: $id,
    data: {
      genre: $genre
      rating: $rating
      description: $description
    })
}

Untuk melakukan beberapa pembaruan, gunakan kolom movie_updateMany.

# Multiple updates (increase all ratings of a genre)
mutation IncreaseRatingForGenre($genre: String!, $rating: Int!) {
  movie_updateMany(
    where: { genre: { eq: $genre } },
    data:
      {
        rating: $rating
      })
}

Menggunakan operasi penambahan, pengurangan, penambahan di akhir, dan penambahan di awal dengan _update

Saat mutasi _update dan _updateMany, Anda dapat menetapkan nilai secara eksplisit di data:, tetapi sering kali lebih masuk akal untuk menerapkan operator seperti kenaikan untuk memperbarui nilai.

Untuk mengubah contoh pembaruan sebelumnya, asumsikan Anda ingin menaikkan rating film tertentu. Anda dapat menggunakan sintaksis rating_update dengan operator inc.

mutation UpdateMovie(
  $id: UUID!,
  $ratingIncrement: Int!
) {
  movie_update(id: $id, data: {
    rating_update: {
      inc: $ratingIncrement
    }
  })
}

Data Connect mendukung operator berikut untuk update kolom:

  • inc untuk menambahkan jenis data Int, Int64, Float, Date, dan Timestamp
  • dec untuk mengurangi jenis data Int, Int64, Float, Date, dan Timestamp

Untuk daftar, Anda juga dapat memperbarui dengan nilai individual atau daftar nilai menggunakan:

  • add untuk menambahkan item jika belum ada ke jenis daftar, kecuali daftar Vektor
  • remove untuk menghapus semua item, jika ada, dari jenis daftar, kecuali daftar Vektor
  • append untuk menambahkan item ke jenis daftar, kecuali daftar Vektor
  • prepend untuk menambahkan item di awal jenis daftar, kecuali daftar Vektor

Melakukan penghapusan

Tentu saja Anda dapat menghapus data film. Pelestari film tentu ingin film fisik dipertahankan selama mungkin.

# Delete by key
mutation DeleteMovie($id: UUID!) {
  movie_delete(id: $id)
}

Di sini, Anda dapat menggunakan _deleteMany.

# Multiple deletes
mutation DeleteUnpopularMovies($minRating: Int!) {
  movie_deleteMany(where: { rating: { le: $minRating } })
}

Menulis mutasi pada relasi

Amati cara menggunakan mutasi _upsert implisit pada relasi.

# Create or update a one to one relation
mutation MovieMetadataUpsert($movieId: UUID!, $director: String!) {
  movieMetadata_upsert(
    data: { movie: { id: $movieId }, director: $director }
  )
}

Mendesain skema untuk mutasi yang efisien

Data Connect menyediakan dua fitur penting yang memungkinkan Anda menulis mutasi yang lebih efisien dan menghemat operasi pulang pergi.

Skalar utama adalah ID objek ringkas yang Data Connect dirakit secara otomatis dari kolom utama dalam skema Anda. Skalar kunci berkaitan dengan efisiensi, yang memungkinkan Anda menemukan informasi tentang identitas dan struktur data dalam satu panggilan. Parameter ini sangat berguna saat Anda ingin melakukan tindakan berurutan pada rekaman baru dan memerlukan ID unik untuk diteruskan ke operasi mendatang, dan juga saat Anda ingin mengakses kunci relasional untuk melakukan operasi tambahan yang lebih kompleks.

Dengan nilai server, Anda dapat secara efektif membiarkan server mengisi kolom dalam tabel Anda secara dinamis menggunakan nilai yang disimpan atau mudah dihitung sesuai dengan ekspresi CEL sisi server tertentu dalam argumen expr. Misalnya, Anda dapat menentukan kolom dengan stempel waktu yang diterapkan saat kolom diakses menggunakan waktu yang disimpan dalam permintaan operasi, updatedAt: Timestamp! @default(expr: "request.time").

Menulis mutasi lanjutan: biarkan Data Connect memberikan nilai menggunakan sintaksis field_expr

Seperti yang dibahas dalam skalar utama dan nilai server, Anda dapat mendesain skema sehingga server mengisi nilai untuk kolom umum seperti id dan tanggal sebagai respons terhadap permintaan klien.

Selain itu, Anda dapat memanfaatkan data, seperti ID pengguna, yang dikirim dalam objek Data Connect request dari aplikasi klien.

Saat menerapkan mutasi, gunakan sintaksis field_expr untuk memicu pembaruan yang dihasilkan server atau mengakses data dari permintaan. Misalnya, untuk meneruskan otorisasi uid yang disimpan dalam permintaan ke operasi _upsert, teruskan "auth.uid" di kolom userId_expr.

# Add a movie to the user's favorites list
mutation AddFavoritedMovie($movieId: UUID!) @auth(level: USER) {
  favorite_movie_upsert(data: { userId_expr: "auth.uid", movieId: $movieId })
}

# Remove a movie from the user's favorites list
mutation DeleteFavoritedMovie($movieId: UUID!) @auth(level: USER) {
  favorite_movie_delete(key: { userId_expr: "auth.uid", movieId: $movieId })
}

Atau, di aplikasi daftar tugas yang sudah dikenal, saat membuat daftar tugas baru, Anda dapat meneruskan id_expr untuk menginstruksikan server agar membuat UUID untuk daftar secara otomatis.

mutation CreateTodoListWithFirstItem(
  $listName: String!
) @transaction {
  # Step 1
  todoList_insert(data: {
    id_expr: "uuidV4()", # <-- auto-generated. Or a column-level @default on `type TodoList` will also work
    name: $listName,
  })
}

Untuk mengetahui informasi selengkapnya, lihat skalar _Expr dalam referensi skalar.

Menulis mutasi lanjutan: operasi multi-langkah

Ada banyak situasi ketika Anda mungkin ingin menyertakan beberapa kolom penulisan (seperti penyisipan) dalam satu mutasi. Anda mungkin juga ingin membaca database selama eksekusi mutasi untuk mencari dan memverifikasi data yang ada sebelum melakukan, misalnya, penyisipan atau pembaruan. Opsi ini menghemat operasi pulang pergi dan oleh karena itu, menghemat biaya.

Data Connect memungkinkan Anda melakukan logika multi-langkah dalam mutasi dengan mendukung:

  • Beberapa kolom penulisan

  • Beberapa kolom baca dalam mutasi Anda (menggunakan kata kunci kolom query).

  • Direktif @transaction, yang menyediakan dukungan transaksi yang sudah dikenal dari database relasional.

  • Direktif @check, yang memungkinkan Anda mengevaluasi isi bacaan menggunakan ekspresi CEL, dan berdasarkan hasil evaluasi tersebut:

    • Melanjutkan pembuatan, pembaruan, dan penghapusan yang ditentukan oleh mutasi
    • Lanjutkan untuk menampilkan hasil kolom kueri
    • Gunakan pesan yang ditampilkan untuk melakukan logika yang sesuai dalam kode klien Anda
  • Direktif @redact, yang memungkinkan Anda menghilangkan hasil kolom kueri dari hasil protokol transfer data.

  • Binding CEL response, yang menyimpan hasil terakumulasi dari semua mutasi dan kueri yang dilakukan dalam operasi multi-langkah yang kompleks. Anda dapat mengakses binding response:

    • Dalam perintah @check, melalui argumen expr:
    • Dengan nilai server, menggunakan sintaksis field_expr

Perintah @transaction

Dukungan untuk mutasi multi-langkah mencakup penanganan error menggunakan transaksi.

Direktif @transaction memastikan bahwa mutasi - dengan satu kolom tulis (misalnya, _insert atau _update) atau dengan beberapa kolom tulis - selalu berjalan dalam transaksi database.

  • Mutasi tanpa @transaction menjalankan setiap kolom root satu demi satu secara berurutan. Operasi ini memunculkan error sebagai error kolom parsial, tetapi tidak dampak dari eksekusi berikutnya.

  • Mutasi dengan @transaction dijamin akan berhasil sepenuhnya atau gagal sepenuhnya. Jika salah satu kolom dalam transaksi gagal, seluruh transaksi akan di-roll back.

Petunjuk @check dan @redact

Perintah @check memverifikasi bahwa kolom yang ditentukan ada dalam hasil kueri. Ekspresi Common Expression Language (CEL) digunakan untuk menguji nilai kolom. Perilaku default direktif adalah memeriksa dan menolak node yang nilainya null atau [] (daftar kosong).

Direktif @redact menyamarkan sebagian respons dari klien. Kolom yang disamarkan masih dievaluasi untuk efek samping (termasuk perubahan data dan @check) dan hasilnya masih tersedia untuk langkah-langkah selanjutnya dalam ekspresi CEL.

Gunakan @check, @check(message:), dan @redact

Penggunaan utama @check dan @redact adalah mencari data terkait untuk memutuskan apakah operasi tertentu harus diizinkan, menggunakan pencarian dalam logika, tetapi menyembunyikannya dari klien. Kueri Anda dapat menampilkan pesan yang berguna untuk penanganan yang benar dalam kode klien.

Sebagai ilustrasi, kolom kueri berikut memeriksa apakah pemohon memiliki peran "admin" yang sesuai untuk melihat pengguna yang dapat mengedit film.

query GetMovieEditors($movieId: UUID!) @auth(level: USER) {
  moviePermission(key: { movieId: $movieId, userId_expr: "auth.uid" }) @redact {
    role @check(expr: "this == 'admin'", message: "You must be an admin to view all editors of a movie.")
  }
  moviePermissions(where: { movieId: { eq: $movieId }, role: { eq: "editor" } }) {
    user {
      id
      username
    }
  }
}

Untuk mempelajari lebih lanjut direktif @check dan @redact dalam pemeriksaan otorisasi, lihat pembahasan pencarian data otorisasi.

Menggunakan @check untuk memvalidasi kunci

Beberapa kolom mutasi, seperti _update, mungkin tidak beroperasi jika tidak ada data dengan kunci yang ditentukan. Demikian pula, pencarian dapat menampilkan nilai null atau daftar kosong. Hal ini tidak dianggap sebagai error sehingga tidak akan memicu rollback.

Untuk mencegah hasil ini, uji apakah kunci dapat ditemukan menggunakan direktif @check.

# Delete by key, error if not found
mutation MustDeleteMovie($id: UUID!) @transaction {
  movie_delete(id: $id) @check(expr: "this != null", message: "Movie not found, therefore nothing is deleted")
}

Menggunakan binding response untuk merangkai mutasi multi-langkah

Pendekatan dasar untuk membuat record terkait, misalnya Movie baru dan entri MovieMetadata terkait, adalah:

  1. Panggil mutasi _insert untuk Movie
  2. Simpan kunci yang ditampilkan dari film yang dibuat
  3. Kemudian, panggil mutasi _insert kedua untuk membuat catatan MovieMetadata.

Namun, dengan Data Connect, Anda dapat menangani kasus umum ini dalam satu operasi multi-langkah dengan mengakses hasil _insert pertama dalam _insert kedua.

Membuat aplikasi ulasan film yang sukses membutuhkan banyak pekerjaan. Mari kita lacak daftar tugas dengan contoh baru.

Menggunakan response untuk menetapkan kolom dengan nilai server

Dalam mutasi daftar tugas berikut:

  • Binding response merepresentasikan objek respons parsial sejauh ini, yang mencakup semua kolom mutasi tingkat teratas sebelum kolom saat ini.
  • Hasil operasi todoList_insert awal, yang menampilkan kolom id (kunci), diakses nanti di response.todoList_insert.id sehingga kita dapat langsung menyisipkan item tugas baru.
mutation CreateTodoListWithFirstItem(
  $listName: String!,
  $itemContent: String!
) @transaction {
  # Sub-step 1:
  todoList_insert(data: {
    id_expr: "uuidV4()", # <-- auto-generated. Or a column-level @default on `type TodoList` will also work
    name: $listName,
  })
  # Sub-step 2:
  todo_insert(data: {
    listId_expr: "response.todoList_insert.id" # <-- Grab the newly generated ID from the partial response so far.
    content: $itemContent,
  })
}

Gunakan response untuk memvalidasi kolom menggunakan @check

response juga tersedia di @check(expr: "..."), sehingga Anda dapat menggunakannya untuk membangun logika sisi server yang lebih rumit. Jika digabungkan dengan query { … } langkah dalam mutasi, Anda dapat mencapai lebih banyak hal tanpa perjalanan pulang pergi klien-server tambahan.

Pada contoh berikut, perhatikan bahwa @check sudah memiliki akses ke response.query karena @check selalu berjalan setelah langkah yang dikaitkan dengannya.

mutation CreateTodoInNamedList(
  $listName: String!,
  $itemContent: String!
) @transaction {
  # Sub-step 1: Look up List.id by its name
  query
  @check(expr: "response.query.todoLists.size() > 0", message: "No such TodoList with the name!")
  @check(expr: "response.query.todoLists.size() < 2", message: "Ambiguous listName!") {
    todoLists(where: { name: $listName }) {
      id
    }
  }
  # Sub-step 2:
  todo_insert(data: {
    listId_expr: "response.todoLists[0].id" # <-- Now we have the parent list ID to insert to
    content: $itemContent,
  })
}

Untuk mengetahui informasi selengkapnya tentang pengikatan response, lihat referensi CEL.

Memahami operasi yang terganggu dengan @transaction dan query @check

Mutasi multi-langkah dapat mengalami error:

  • Operasi database mungkin gagal.
  • Logika kueri @check dapat menghentikan operasi.

Data Connect merekomendasikan agar Anda menggunakan direktif @transaction dengan mutasi multi-langkah. Hal ini menghasilkan database yang lebih konsisten dan hasil mutasi yang lebih mudah ditangani dalam kode klien:

  • Pada error atau @check yang gagal pertama, operasi akan dihentikan, sehingga tidak perlu mengelola eksekusi kolom berikutnya atau evaluasi CEL.
  • Rollback dilakukan sebagai respons terhadap error database atau logika @check, sehingga menghasilkan status database yang konsisten.
  • Error rollback selalu ditampilkan ke kode klien.

Mungkin ada beberapa kasus penggunaan saat Anda memilih untuk tidak menggunakan @transaction: Anda dapat memilih konsistensi pada akhirnya jika, misalnya, Anda memerlukan throughput, skalabilitas, atau ketersediaan yang lebih tinggi. Namun, Anda perlu mengelola database dan kode klien untuk memungkinkan hasil:

  • Jika satu kolom gagal karena operasi database, kolom berikutnya akan terus dieksekusi. Namun, @check yang gagal tetap akan menghentikan seluruh operasi.
  • Rollback tidak dilakukan, yang berarti status database campuran dengan beberapa pembaruan berhasil dan beberapa pembaruan gagal.
  • Operasi Anda dengan @check dapat memberikan hasil yang lebih tidak konsisten jika logika @check Anda menggunakan hasil pembacaan dan/atau penulisan pada langkah sebelumnya.
  • Hasil yang ditampilkan ke kode klien akan berisi campuran respons berhasil dan gagal yang lebih kompleks untuk ditangani.

Petunjuk untuk mutasi Data Connect

Selain direktif yang Anda gunakan dalam menentukan jenis dan tabel, Data Connect menyediakan direktif @auth, @check, @redact, dan @transaction untuk meningkatkan perilaku operasi.

Perintah Berlaku untuk Deskripsi
@auth Kueri dan mutasi Menentukan kebijakan otorisasi untuk kueri atau mutasi. Lihat panduan otorisasi dan pengesahan.
@check Kolom query dalam operasi multi-langkah Memverifikasi bahwa kolom yang ditentukan ada dalam hasil kueri. Ekspresi Common Expression Language (CEL) digunakan untuk menguji nilai kolom. Lihat Operasi multi-langkah.
@redact Kueri Menyembunyikan bagian respons dari klien. Lihat Operasi multi-langkah.
@transaction Mutasi Memastikan bahwa mutasi selalu berjalan dalam transaksi database. Lihat Operasi multi-langkah.

Langkah berikutnya

Anda mungkin tertarik dengan: