Firebase Data Connect を使用すると、Google Cloud SQL で管理されている PostgreSQL インスタンスのコネクタを作成できます。これらのコネクタは、データを使用するためのスキーマ、クエリ、ミューテーションの組み合わせです。
スタートガイドでは、PostgreSQL の映画レビュー アプリのスキーマについて説明しました。このガイドでは、PostgreSQL の Data Connect スキーマを設計する方法について詳しく説明します。
このガイドでは、Data Connect クエリとミューテーションをスキーマの例と組み合わせて説明します。Data Connect スキーマに関するガイドでクエリ(およびミューテーション)について説明する理由他の GraphQL ベースのプラットフォームと同様に、Firebase Data Connect はクエリファーストの開発プラットフォームです。そのため、デベロッパーはデータモデリングでクライアントが必要とするデータについて検討する必要があります。これは、プロジェクト用に開発するデータスキーマに大きな影響を与えます。
このガイドでは、まず新しい映画レビューのスキーマについて説明します。次に、そのスキーマから派生したクエリとミューテーションについて説明します。最後に、コア Data Connect スキーマと同等の SQL リストを示します。
映画レビュー アプリのスキーマ
ユーザーが映画のレビューを送信して表示できるサービスを構築するとします。
このようなアプリには初期スキーマが必要です。このスキーマは後で拡張して、複雑なリレーショナル クエリを作成します。
映画の表
映画のスキーマには、次のようなコア ディレクティブが含まれています。
@table
:singular
引数とplural
引数を使用してオペレーション名を設定できます。@col
: 列名を明示的に設定する@default
: デフォルトの設定を許可します。
# Movies
type Movie
@table(name: "Movies", singular: "movie", plural: "movies", key: ["id"]) {
id: UUID! @col(name: "movie_id") @default(expr: "uuidV4()")
title: String!
releaseYear: Int @col(name: "release_year")
genre: String
rating: Int @col(name: "rating")
description: String @col(name: "description")
}
サーバー値とキースカラー
映画レビュー アプリについて説明する前に、Data Connect サーバー値とキー スカラーについて説明します。
サーバー値を使用すると、特定のサーバーサイド式に従って、保存されている値またはすぐに計算できる値を使用して、テーブル内のフィールドにサーバー側で動的に値を入力できます。たとえば、式 updatedAt: Timestamp! @default(expr: "request.time")
を使用して、フィールドにアクセスされたときにタイムスタンプが適用されるフィールドを定義できます。
キー スカラーは、スキーマ内のキーフィールドから Data Connect によって自動的にアセンブルされる簡潔なオブジェクト識別子です。キースカラーは効率性を重視しており、1 回の呼び出しでデータの ID と構造に関する情報を取得できます。これは、新しいレコードに対して連続的なアクションを実行し、今後のオペレーションに渡す一意の識別子が必要である場合に特に便利です。また、リレーショナル キーにアクセスして、より複雑な追加オペレーションを実行する場合にも便利です。
映画のメタデータ テーブル
次に、映画監督を追跡し、Movie
と 1 対 1 の関係を設定します。
@ref
ディレクティブを追加して、関係を定義します。
# Movie Metadata
# Movie - MovieMetadata is a one-to-one relationship
type MovieMetadata
@table(
name: "MovieMetadata"
) {
# @ref creates a field in the current table (MovieMetadata) that holds the
# primary key of the referenced type
# In this case, @ref(fields: "id") is implied
movie: Movie! @ref
# movieId: UUID <- this is created by the above @ref
director: String @col(name: "director")
}
Actor と MovieActor
次に、俳優が映画に出演するようにします。映画と俳優の間には多対多の関係があるため、結合テーブルを作成します。
# Actors
# Suppose an actor can participate in multiple movies and movies can have multiple actors
# Movie - Actors (or vice versa) is a many to many relationship
type Actor @table(name: "Actors", singular: "actor", plural: "actors") {
id: UUID! @col(name: "actor_id") @default(expr: "uuidV4()")
name: String! @col(name: "name", dataType: "varchar(30)")
}
# Join table for many-to-many relationship for movies and actors
# The 'key' param signifies the primary key(s) of this table
# In this case, the keys are [movieId, actorId], the generated fields of the reference types [movie, actor]
type MovieActor @table(key: ["movie", "actor"]) {
# @ref creates a field in the current table (MovieActor) that holds the primary key of the referenced type
# In this case, @ref(fields: "id") is implied
movie: Movie! @ref
# movieId: UUID! <- this is created by the above @ref, see: implicit.gql
actor: Actor! @ref
# actorId: UUID! <- this is created by the above @ref, see: implicit.gql
role: String! @col(name: "role") # "main" or "supporting"
# optional other fields
}
ユーザー
最後に、アプリのユーザーです。
# Users
# Suppose a user can leave reviews for movies
# user:reviews is a one to many relationship, movie:reviews is a one to many relationship, movie:user is a many to many relationship
type User
@table(name: "Users", singular: "user", plural: "users", key: ["id"]) {
id: UUID! @col(name: "user_id") @default(expr: "uuidV4()")
auth: String @col(name: "user_auth") @default(expr: "auth.uid")
username: String! @col(name: "username", dataType: "varchar(30)")
# The following are generated from the @ref in the Review table
# reviews_on_user
# movies_via_Review
}
サポートされるデータタイプ
Data Connect は、@col(dataType:)
を使用して PostgreSQL 型に割り当てられる次のスカラー データ型をサポートしています。
Data Connect 型 | GraphQL 組み込み型または Data Connect カスタム型 |
デフォルトの PostgreSQL タイプ | サポートされている PostgreSQL 型 (かっこ内のエイリアス) |
---|---|---|---|
文字列 | GraphQL | テキスト | text bit(n), varbit(n) char(n), varchar(n) |
Int | GraphQL | 整数 | Int2(smallint、smallserial)、 int4(integer、int、serial) |
浮動小数点数 | GraphQL | float8 | float4(実数) float8(倍精度) 数値(小数) |
ブール値 | GraphQL | ブール値 | ブール値 |
UUID | カスタム | uuid | uuid |
Int64 | カスタム | bigint | int8(bigint、bigserial) 数値(小数) |
日付 | カスタム | date | 日付 |
タイムスタンプ | カスタム | timestamptz | timestamptz 注: ローカルのタイムゾーン情報は保存されません。 |
ベクトル | カスタム | ベクトル | ベクトル Vertex AI でベクトル類似性検索を実行するをご覧ください。 |
- GraphQL の
List
は 1 次元配列にマッピングされます。- たとえば、
[Int]
はint5[]
にマッピングされ、[Any]
はjsonb[]
にマッピングされます。 - Data Connect はネストされた配列をサポートしていません。
- たとえば、
暗黙のクエリと事前定義されたクエリとミューテーション
Data Connect クエリとミューテーションは、スキーマ内の型と型の関係に基づいて Data Connect によって生成された一連の暗黙的なクエリと暗黙的なミューテーションを拡張します。暗黙的なクエリとミューテーションは、スキーマを編集するたびにローカル ツールによって生成されます。
開発プロセスでは、これらの暗黙的なオペレーションに基づいて、事前定義されたクエリと事前定義されたミューテーションを実装します。
暗黙的なクエリとミューテーションの名前付け
Data Connect は、スキーマ型宣言から暗黙的なクエリとミューテーションに適した名前を推論します。たとえば、PostgreSQL ソースで Movie
という名前のテーブルを定義すると、サーバーは暗黙的に次のものを生成します。
- 単一テーブルのユースケースのクエリ。わかりやすい名前は
movie
(単数形、eq
などの引数を渡して個々の結果を取得する場合)とmovies
(複数形、gt
などの引数とorderby
などのオペレーションを渡して結果リストを取得する場合)です。Data Connect は、actors_on_movies
やactors_via_actormovie
などの明示的な名前を持つマルチテーブル リレーショナル オペレーションのクエリも生成します。 movie_insert
、movie_upsert
という名前のミューテーション
スキーマ定義言語では、singular
ディレクティブ引数と plural
ディレクティブ引数を使用して、オペレーションの名前を明示的に設定することもできます。
クエリとミューテーションのディレクティブ
Data Connect には、型とテーブルの定義に使用するディレクティブに加えて、クエリとミューテーションの動作を拡張するための @auth
、@check
、@redact
、@transaction
ディレクティブが用意されています。
ディレクティブ | 対象 | 説明 |
---|---|---|
@auth |
クエリとミューテーション | クエリまたはミューテーションの認証ポリシーを定義します。承認と構成証明のガイドをご覧ください。 |
@check |
認可データのルックアップ クエリ | 指定したフィールドがクエリ結果に存在することを確認します。フィールド値をテストするには、Common Expression Language(CEL)式を使用します。承認と構成証明のガイドをご覧ください。 |
@redact |
クエリ | クライアントからのレスポンスの一部を削除します。承認と構成証明のガイドをご覧ください。 |
@transaction |
ミューテーション | ミューテーションが常にデータベース トランザクション内で実行されるようにします。映画アプリのミューテーションの例をご覧ください。 |
映画レビュー データベースに対するクエリ
Data Connect クエリは、クエリ オペレーション タイプの宣言、オペレーション名、0 個以上のオペレーション引数、引数を持つ 0 個以上のディレクティブで定義します。
クイックスタートの例の listEmails
クエリではパラメータを使用しませんでした。もちろん、多くの場合、クエリ フィールドに渡されるデータは動的になります。$variableName
構文を使用すると、クエリ定義のコンポーネントの 1 つとして変数を操作できます。
したがって、次のクエリには次のものがあります。
query
型定義ListMoviesByGenre
オペレーション(クエリ)名- 単一変数の
$genre
オペレーション引数 - 単一のディレクティブ
@auth
。
query ListMoviesByGenre($genre: String!) @auth(level: USER)
すべてのクエリ引数には、型宣言、String
などの組み込み型、または Movie
などのスキーマ定義のカスタム型が必要です。
複雑なクエリのシグネチャを見てみましょう。最後に、暗黙的なクエリで使用できる強力で簡潔な関係式について説明します。この式は、事前定義されたクエリで構築できます。
クエリ内の主なスカラー
ただし、まずキー スカラーについて説明します。
Data Connect は、_Key
で識別されるキー スカラーの特殊な型を定義します。たとえば、Movie
テーブルのキー スカラーの型は Movie_Key
です。
キーのスケールは、ほとんどの暗黙的なミューテーションによって返されるレスポンスとして取得します。もちろん、スケールキーの作成に必要なすべてのフィールドを取得したクエリからも取得できます。
実行例の movie
などの単一の自動クエリは、キー スカラーを受け入れることができるキー引数をサポートしています。
キー スカラーをリテラルとして渡すことができます。ただし、キー スカラーを入力として渡す変数を定義できます。
query GetMovie($myKey: Movie_Key!) {
movie(key: $myKey) { title }
}
これらは、次のようなリクエスト JSON(または他のシリアル化形式)で指定できます。
{
# …
"variables": {
"myKey": {"foo": "some-string-value", "bar": 42}
}
}
カスタム スカラー解析により、オブジェクト構文を使用して Movie_Key
を作成することもできます。この構文には変数を含めることができます。これは、なんらかの理由で個々のコンポーネントを異なる変数に分割する場合に特に便利です。
クエリでのエイリアス
Data Connect は、クエリでの GraphQL エイリアシングをサポートしています。エイリアスを使用すると、クエリの結果で返されるデータの名前を変更できます。1 つの Data Connect クエリで、複数のフィルタやその他のクエリ オペレーションを 1 つの効率的なリクエストでサーバーに適用し、複数の「サブクエリ」を一度に効果的に実行できます。返されるデータセットで名前の競合を回避するには、エイリアスを使用してサブクエリを区別します。
式で別名 mostPopular
を使用するクエリを次に示します。
query ReviewTopPopularity($genre: String) {
mostPopular: review(first: {
where: {genre: {eq: $genre}},
orderBy: {popularity: DESC}
}) { … }
}
フィルタを使用した簡単なクエリ
Data Connect クエリは、一般的な SQL フィルタと並べ替え演算にマッピングされます。
where
演算子と orderBy
演算子(単数形、複数形のクエリ)
テーブル内の一致するすべての行(およびネストされた関連付け)を返します。フィルタに一致するレコードがない場合、空の配列を返します。
query MovieByTopRating($genre: String) {
mostPopular: movies(
where: { genre: { eq: $genre } }, orderBy: { rating: DESC }
) {
# graphql: list the fields from the results to return
id
title
genre
description
}
}
query MoviesByReleaseYear($min: Int, $max: Int) {
movies(where: {releaseYear: {le: $max, ge: $min}}, orderBy: [{releaseYear: ASC}]) { … }
}
limit
演算子と offset
演算子(単数形、複数形のクエリ)
結果のページ分けを行うことができます。これらの引数は受け入れられますが、結果には返されません。
query MoviesTop10 {
movies(orderBy: [{ rating: DESC }], limit: 10) {
# graphql: list the fields from the results to return
title
}
}
配列フィールドの includes
配列フィールドに指定されたアイテムが含まれていることをテストできます。
# Filter using arrays and embedded fields.
query ListMoviesByTag($tag: String!) {
movies(where: { tags: { includes: $tag }}) {
# graphql: list the fields from the results to return
id
title
}
}
文字列演算と正規表現
クエリでは、正規表現など、一般的な文字列検索と比較オペレーションを使用できます。効率性を高めるために、ここでは複数のオペレーションをバンドルし、エイリアスで曖昧さを解消しています。
query MoviesTitleSearch($prefix: String, $suffix: String, $contained: String, $regex: String) {
prefixed: movies(where: {title: {startsWith: $prefix}}) {...}
suffixed: movies(where: {title: {endsWith: $suffix}}) {...}
contained: movies(where: {title: {contains: $contained}}) {...}
matchRegex: movies(where: {title: {pattern: {regex: $regex}}}) {...}
}
or
と and
(合成フィルタ用)
より複雑なロジックには or
と and
を使用します。
query ListMoviesByGenreAndGenre($minRating: Int!, $genre: String) {
movies(
where: { _or: [{ rating: { ge: $minRating } }, { genre: { eq: $genre } }] }
) {
# graphql: list the fields from the results to return
title
}
}
複雑なクエリ
Data Connect クエリは、テーブル間のリレーションに基づいてデータにアクセスできます。スキーマで定義されたオブジェクト(1 対 1)または配列(1 対多)のリレーションを使用して、ネストされたクエリを作成できます。つまり、1 つのタイプのデータとともに、ネストされたタイプまたは関連するタイプのデータを取得できます。
このようなクエリでは、生成された暗黙的なクエリでマジック Data Connect _on_
構文と _via
構文が使用されます。
初期バージョンのスキーマを変更します。
多対 1
Review
テーブルと User
の変更により、アプリにレビューを追加しましょう。
# Users
# Suppose a user can leave reviews for movies
# user:reviews is a one to many relationship,
# movie:reviews is a one to many relationship,
# movie:user is a many to many relationship
type User
@table(name: "Users", singular: "user", plural: "users", key: ["id"]) {
id: UUID! @col(name: "user_id") @default(expr: "uuidV4()")
auth: String @col(name: "user_auth") @default(expr: "auth.uid")
username: String! @col(name: "username", dataType: "varchar(30)")
# The following are generated from the @ref in the Review table
# reviews_on_user
# movies_via_Review
}
# Reviews
type Review @table(name: "Reviews", key: ["movie", "user"]) {
id: UUID! @col(name: "review_id") @default(expr: "uuidV4()")
user: User! @ref
movie: Movie! @ref
rating: Int
reviewText: String
reviewDate: Date! @default(expr: "request.time")
}
多対 1 のクエリ
エイリアスを使用したクエリを見て、_via_
構文を説明しましょう。
query UserMoviePreferences($username: String!) @auth(level: USER) {
users(where: { username: { eq: $username } }) {
likedMovies: movies_via_review(where: { rating: { ge: 4 } }) {
title
genre
description
}
dislikedMovies: movies_via_review(where: { rating: { le: 2 } }) {
title
genre
description
}
}
}
1 対 1
パターンが確認できます。以下では、説明のためにスキーマを変更しています。
# Movies
type Movie
@table(name: "Movies", singular: "movie", plural: "movies", key: ["id"]) {
id: UUID! @col(name: "movie_id") @default(expr: "uuidV4()")
title: String!
releaseYear: Int @col(name: "release_year")
genre: String
rating: Int @col(name: "rating")
description: String @col(name: "description")
tags: [String] @col(name: "tags")
}
# Movie Metadata
# Movie - MovieMetadata is a one-to-one relationship
type MovieMetadata
@table(
name: "MovieMetadata"
) {
# @ref creates a field in the current table (MovieMetadata) that holds the primary key of the referenced type
# In this case, @ref(fields: "id") is implied
movie: Movie! @ref
# movieId: UUID <- this is created by the above @ref
director: String @col(name: "director")
}
extend type MovieMetadata {
movieId: UUID! # matches primary key of referenced type
...
}
extend type Movie {
movieMetadata: MovieMetadata # can only be non-nullable on ref side
# conflict-free name, always generated
movieMetadatas_on_movie: MovieMetadata
}
1 対 1 のクエリ
_on_
構文を使用してクエリを実行できます。
# One to one
query GetMovieMetadata($id: UUID!) @auth(level: PUBLIC) {
movie(id: $id) {
movieMetadatas_on_movie {
director
}
}
}
多対多
映画には俳優が必要で、俳優には映画が必要です。これらは多対多のリレーションシップであり、MovieActors
結合テーブルでモデル化できます。
# MovieActors Join Table Definition
type MovieActors @table(
key: ["movie", "actor"] # join key triggers many-to-many generation
) {
movie: Movie!
actor: Actor!
}
# generated extensions for the MovieActors join table
extend type MovieActors {
movieId: UUID!
actorId: UUID!
}
# Extensions for Actor and Movie to handle many-to-many relationships
extend type Movie {
movieActors: [MovieActors!]! # standard many-to-one relation to join table
actors: [Actor!]! # many-to-many via join table
movieActors_on_actor: [MovieActors!]!
# since MovieActors joins distinct types, type name alone is sufficiently precise
actors_via_MovieActors: [Actor!]!
}
extend type Actor {
movieActors: [MovieActors!]! # standard many-to-one relation to join table
movies: [Movie!]! # many-to-many via join table
movieActors_on_movie: [MovieActors!]!
movies_via_MovieActors: [Movie!]!
}
多対多のクエリ
エイリアスを使用したクエリを見てみましょう。_via_
構文について説明します。
query GetMovieCast($movieId: UUID!, $actorId: UUID!) @auth(level: PUBLIC) {
movie(id: $movieId) {
mainActors: actors_via_MovieActor(where: { role: { eq: "main" } }) {
name
}
supportingActors: actors_via_MovieActor(
where: { role: { eq: "supporting" } }
) {
name
}
}
actor(id: $actorId) {
mainRoles: movies_via_MovieActor(where: { role: { eq: "main" } }) {
title
}
supportingRoles: movies_via_MovieActor(
where: { role: { eq: "supporting" } }
) {
title
}
}
}
映画レビュー データベースのミューテーション
前述のように、スキーマでテーブルを定義すると、Data Connect は各テーブルの基本的な暗黙的なミューテーションを生成します。
type Movie @table { ... }
extend type Mutation {
# Insert a row into the movie table.
movie_insert(...): Movie_Key!
# Upsert a row into movie."
movie_upsert(...): Movie_Key!
# Update a row in Movie. Returns null if a row with the specified id/key does not exist
movie_update(...): Movie_Key
# Update rows based on a filter in Movie.
movie_updateMany(...): Int!
# Delete a single row in Movie. Returns null if a row with the specified id/key does not exist
movie_delete(...): Movie_Key
# Delete rows based on a filter in Movie.
movie_deleteMany(...): Int!
}
これにより、コア CRUD ケースの複雑さを増やすことができます。これを 5 回続けて言ってみましょう。
@transaction
ディレクティブ
このディレクティブにより、ミューテーションは常にデータベース トランザクション内で実行されます。
@transaction
を使用したミューテーションは、完全に成功するか、完全に失敗するかのどちらかになります。トランザクション内のいずれかのフィールドでエラーが発生すると、トランザクション全体がロールバックされます。クライアントの観点から、失敗は、リクエスト全体がリクエスト エラーで失敗し、実行が開始されていない場合と同様に動作します。
@transaction
のないミューテーションは、各ルートフィールドを順番に実行します。エラーは部分フィールド エラーとして表示されますが、後続の実行への影響は表示されません。
作成
基本的な作成を行いましょう。
# 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
})
}
または 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"
})
}
更新を実行する
最新情報は次のとおりです。プロデューサーと監督は、平均評価がトレンドに沿っていることを願っています。
mutation UpdateMovie(
$id: UUID!,
$genre: String!,
$rating: Int!,
$description: String!
) {
movie_update(id: $id, data: {
genre: $genre
rating: $rating
description: $description
})
}
# Multiple updates (increase all ratings of a genre)
mutation IncreaseRatingForGenre($genre: String!, $ratingIncrement: Int!) {
movie_updateMany(
where: { genre: { eq: $genre } },
update: { rating: { inc: $ratingIncrement } }
)
}
削除を実行する
もちろん、映画データは削除できます。映画の保存者は、物理的なフィルムをできるだけ長く維持したいと考えています。
# Delete by key
mutation DeleteMovie($id: UUID!) {
movie_delete(id: $id)
}
ここでは _deleteMany
を使用できます。
# Multiple deletes
mutation DeleteUnpopularMovies($minRating: Int!) {
movie_deleteMany(where: { rating: { le: $minRating } })
}
リレーションにミューテーションを書き込む
関係で暗黙的な _upsert
ミューテーションを使用する方法を確認します。
# Create or update a one to one relation
mutation MovieMetadataUpsert($movieId: UUID!, $director: String!) {
movieMetadata_upsert(
data: { movie: { id: $movieId }, director: $director }
)
}
認可データのルックアップ クエリ
Data Connect ミューテーションを承認するには、まずデータベースにクエリを実行し、CEL 式でクエリの結果を確認します。これは、テーブルに書き込みを行う際に、別のテーブルの行の内容を確認する必要がある場合に便利です。
この機能は次のものをサポートしています。
@check
ディレクティブ: フィールドの内容を評価し、その評価結果に基づいて次の処理を行います。- ミューテーションで定義された作成、更新、削除を続行します。
- クエリによってクライアントに返された値を使用して、クライアントでさまざまなロジックを実行する
@redact
ディレクティブ。ワイヤー プロトコルの結果からクエリ結果を省略できます。
これらの機能は、認可フローに役立ちます。
同等の SQL スキーマ
これは例示のみを目的としています。-- Movies Table
CREATE TABLE Movies (
movie_id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
title VARCHAR(255) NOT NULL,
release_year INT,
genre VARCHAR(30),
rating INT,
description TEXT,
tags TEXT[]
);
-- Movie Metadata Table
CREATE TABLE MovieMetadata (
movie_id UUID REFERENCES Movies(movie_id) UNIQUE,
director VARCHAR(255) NOT NULL,
PRIMARY KEY (movie_id)
);
-- Actors Table
CREATE TABLE Actors (
actor_id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
name VARCHAR(30) NOT NULL
);
-- MovieActor Join Table for Many-to-Many Relationship
CREATE TABLE MovieActor (
movie_id UUID REFERENCES Movies(movie_id),
actor_id UUID REFERENCES Actors(actor_id),
role VARCHAR(50) NOT NULL, # "main" or "supporting"
PRIMARY KEY (movie_id, actor_id),
FOREIGN KEY (movie_id) REFERENCES Movies(movie_id),
FOREIGN KEY (actor_id) REFERENCES Actors(actor_id)
);
-- Users Table
CREATE TABLE Users (
user_id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
user_auth VARCHAR(255) NOT NULL
username VARCHAR(30) NOT NULL
);
-- Reviews Table
CREATE TABLE Reviews (
review_id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
user_id UUID REFERENCES Users(user_id),
movie_id UUID REFERENCES Movies(movie_id),
rating INT,
review_text TEXT,
review_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE (movie_id, user_id)
FOREIGN KEY (user_id) REFERENCES Users(user_id),
FOREIGN KEY (movie_id) REFERENCES Movies(movie_id)
);
-- Self Join Example for Movie Sequel Relationship
ALTER TABLE Movies
ADD COLUMN sequel_to UUID REFERENCES Movies(movie_id);
次のステップ
- 認可と構成証明を使用してクエリとミューテーションを保護する方法について学習する。
- 自動生成された ウェブ SDK、Android SDK、iOS SDK、Flutter SDK からクエリとミューテーションを呼び出す方法を学びます。