Menerapkan mutasi SQL Connect

Firebase SQL 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. SQL Connect mengintegrasikan autentikasi dan otorisasi ke dalam mutasi ini, dan menghasilkan 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 menjalankannya di Firebase konsol, dari lingkungan yang memiliki hak istimewa menggunakan Firebase Admin SDK, dan di lingkungan pengembangan lokal menggunakan ekstensi SQL Connect VS Code.

Panduan ini membahas lebih mendalam tentang mutasi yang dapat di-deploy.

Fitur mutasi SQL Connect

SQL 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 SQL Connect ke GraphQL, Anda dapat menerapkan mutasi lanjutan untuk aplikasi yang lebih cepat dan lebih efisien:

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

Menggunakan kolom yang dihasilkan untuk menerapkan mutasi

Operasi SQL Connect akan memperluas kumpulan kolom yang otomatis dihasilkan oleh SQL Connect berdasarkan jenis dan hubungan jenis dalam skema Anda. Kolom ini dihasilkan oleh alat lokal setiap kali Anda mengedit skema.

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

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

Mutasi dengan
movie_insert kolom

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

Gunakan kolom ini untuk membuat satu film.

mutation CreateMovie($title: String!, $releaseYear: Int!, $genre: String!, $rating: Int!) {
  movie_insert(data: {
    title: $title
    releaseYear: $releaseYear
    genre: $genre
    rating: $rating
  })
}

Mutasi dengan
movie_update kolom

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!, $genre: String, $rating: Int, $description: String) {
  movie_update(key: $myKey, data: {
    genre: $genre
    rating: $rating
    description: $description
  })
}

Mutasi dengan
movie_delete kolom

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

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

SQL Connect memperluas kueri GraphQL dengan perintah yang disesuaikan seperti @auth dan @transaction.

Jadi, mutasi berikut memiliki:

  • Definisi jenis mutation
  • Nama operasi (mutasi) SignUp
  • Satu argumen operasi variabel $username
  • Satu perintah, @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 data individual dari database.

Buat

Mari 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"
  })
}

Lakukan pembaruan

Berikut adalah pembaruan. Produser dan sutradara tentu berharap rating rata-rata tersebut sedang tren.

Kolom movie_update berisi argumen id yang diharapkan untuk mengidentifikasi data 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, dan penambahan dengan _update

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

Untuk mengubah contoh pembaruan sebelumnya, asumsikan Anda ingin menambahkan 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
    }
  })
}

SQL Connect mendukung operator berikut untuk pembaruan 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 ke jenis daftar, kecuali daftar Vektor

Lakukan 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

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

Skalar kunci adalah ID objek ringkas yang SQL Connect otomatis dirakit dari kolom kunci dalam skema Anda. Skalar kunci adalah tentang efisiensi, yang memungkinkan Anda menemukan informasi tentang identitas dan struktur data dalam satu panggilan. Skalar kunci sangat berguna saat Anda ingin melakukan tindakan berurutan pada data 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 menggunakan nilai server, Anda dapat secara efektif mengizinkan server mengisi kolom dalam tabel 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: mengizinkan SQL Connect menyediakan nilai menggunakan field_expr sintaksis

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

Selain itu, Anda dapat menggunakan data, seperti ID pengguna, yang dikirim dalam SQL Connect request objek 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, dalam aplikasi daftar tugas yang sudah dikenal, saat membuat daftar tugas baru, Anda dapat meneruskan id_expr untuk menginstruksikan server agar otomatis membuat UUID untuk daftar tersebut.

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 tulis (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.

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

  • Beberapa kolom tulis

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

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

  • Perintah @check directive, yang memungkinkan Anda mengevaluasi konten baca menggunakan ekspresi CEL, dan berdasarkan hasil evaluasi tersebut:

    • Lanjutkan dengan 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
  • Perintah @redact, yang memungkinkan Anda menghapus hasil kolom kueri dari hasil protokol kabel.

  • Binding response CEL, yang menyimpan hasil kumulatif 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.

Perintah @transaction mewajibkan 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 per satu secara berurutan. Operasi ini menampilkan error sebagai error kolom parsial, tetapi tidak menampilkan 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.

Perintah @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 perintah ini adalah memeriksa dan menolak node yang nilainya adalah null atau [] (daftar kosong).

Perintah @redact menghapus sebagian respons dari klien. Kolom yang dihapus masih dievaluasi untuk efek samping (termasuk perubahan data dan @check) dan hasilnya masih tersedia untuk langkah-langkah berikutnya dalam ekspresi CEL.

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

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

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 perintah @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 data dengan kunci yang ditentukan tidak ada. Demikian pula, pencarian mungkin menampilkan null atau daftar kosong. Hal ini tidak dianggap sebagai error dan oleh karena itu tidak akan memicu rollback.

Untuk mencegah hasil ini, uji apakah kunci dapat ditemukan menggunakan perintah @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 menggabungkan mutasi multi-langkah

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

  1. Memanggil mutasi _insert untuk Movie
  2. Menyimpan kunci yang ditampilkan dari film yang dibuat
  3. Kemudian, memanggil mutasi _insert kedua untuk membuat data MovieMetadata.

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

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

Menggunakan response untuk menetapkan kolom dengan nilai server

Dalam mutasi daftar tugas berikut:

  • Binding response mewakili objek respons parsial sejauh ini, yang mencakup semua kolom mutasi tingkat atas sebelum kolom saat ini.
  • Hasil operasi todoList_insert awal, yang menampilkan 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,
  })
}

Menggunakan response untuk memvalidasi kolom menggunakan @check

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

Dalam contoh berikut, perhatikan: bahwa @check sudah memiliki akses ke response.query karena @check selalu berjalan setelah langkah yang dilampirkan.

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 binding 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.

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

  • Pada error pertama atau @check yang gagal, 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 eventual 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 menghentikan seluruh operasi.
  • Rollback tidak dilakukan, yang berarti status database campuran dengan beberapa pembaruan yang berhasil dan beberapa pembaruan yang gagal.
  • Operasi Anda dengan @check dapat memberikan hasil yang lebih tidak konsisten jika logika @check menggunakan hasil pembacaan dan/atau penulisan pada langkah sebelumnya.
  • Hasil yang ditampilkan ke kode klien akan berisi campuran respons keberhasilan dan kegagalan yang lebih kompleks untuk ditangani.

Perintah untuk mutasi SQL Connect

Selain perintah yang Anda gunakan dalam menentukan jenis dan tabel, SQL Connect menyediakan perintah @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 Menghapus sebagian respons dari klien. Lihat Operasi multi-langkah.
@transaction Mutasi Mewajibkan mutasi untuk selalu berjalan dalam transaksi database. Lihat Operasi multi-langkah.

Langkah berikutnya

Anda mungkin tertarik dengan: