データの保存

データの保存方法

PUT 定義されたパスにデータの書き込みや置換を行います(例: fireblog/users/user1/<data>)。
PATCH データのすべてを置換することなく、定義済みのパスのキーの一部を更新します。
POST Firebase データベース内のデータのリストに追加します。POST リクエストを送信するたびに、Firebase クライアントから fireblog/users/<unique-id>/<data> のような一意のキーが生成されます。
DELETE 指定した Firebase データベース参照からデータを削除します。

PUT によるデータの書き込み

REST API による基本的な書き込み操作は PUT です。ここでは、データの保存方法を示すために、投稿とユーザーが含まれるブログ アプリケーションを構築します。このアプリケーションのすべてのデータは、Firebase データベースの URL「https://docs-examples.firebaseio.com/fireblog」の「fireblog」のパスに保存されます。

Firebase データベースにいくつかのユーザーデータを保存するところから始めましょう。一意のユーザー名を使用して各ユーザーを保存します。また、ユーザーの氏名と生年月日も保存します。各ユーザーが一意のユーザー名を持ち、これがキーとなって別途作成する必要がないため、ここでは POST ではなく PUT を使用するのが適しています。

PUT を使用すると、文字列、数字、ブール値、配列、または任意の JSON オブジェクトを Firebase データベースに書き込むことができます。次の例では、オブジェクトを渡します。

curl -X PUT -d '{
  "alanisawesome": {
    "name": "Alan Turing",
    "birthday": "June 23, 1912"
  }
}' 'https://docs-examples.firebaseio.com/fireblog/users.json'

JSON オブジェクトがデータベースに保存されると、オブジェクトのプロパティが子の場所にネスト式に自動マッピングされます。新しく作成されたノードに移動すると、値「Alan Turing」が表示されます。また、データを子の場所に直接保存することもできます。

curl -X PUT -d '"Alan Turing"' \
  'https://docs-examples.firebaseio.com/fireblog/users/alanisawesome/name.json'
curl -X PUT -d '"June 23, 1912"' \
  'https://docs-examples.firebaseio.com/fireblog/users/alanisawesome/birthday.json'

上述の 2 つの例、つまり、値をオブジェクトと同時に書き込む場合と、子の場所に別個に書き込む場合では、結果として同じデータが Firebase データベースに保存されることになります。

{
  "users": {
    "alanisawesome": {
      "date_of_birth": "June 23, 1912",
      "full_name": "Alan Turing"
    }
  }
}

成功したリクエストは 200 OK HTTP ステータス コードで示されます。レスポンスには、データベースに書き込んだデータが含まれています。最初の例では、データを監視しているクライアントで 1 つのイベントのみがトリガーされますが、2 番目の例では 2 つのイベントがトリガーされます。データがパス users にすでに存在する場合、最初の方法ではデータが上書きされることに注意してください。これに対し、2 番目の方法では、個々の子ノードの値のみが変更され、他の子は変更されません。PUT は JavaScript SDK の set() と同義です。

PATCH によるデータの更新

PATCH リクエストを使用すると、既存のデータを上書きすることなく、特定の場所の特定の子を更新できます。それでは、PATCH リクエストを使用して Turing のユーザーデータにニックネームを追加してみましょう。

curl -X PATCH -d '{
  "nickname": "Alan The Machine"
}' \
  'https://docs-examples.firebaseio.com/fireblog/users/alanisawesome.json'

上記のリクエストは、子 namebirthday を削除することなく、alanisawesome オブジェクトに nickname を追加します。代わりに PUT リクエストを発行した場合、namebirthday はリクエストに含まれないため削除されています。これで、Firebase データベースのデータが次のようになります。

{
  "users": {
    "alanisawesome": {
      "date_of_birth": "June 23, 1912",
      "full_name": "Alan Turing",
      "nickname": "Alan The Machine"
    }
  }
}

成功したリクエストは 200 OK HTTP ステータス コードで示されます。応答には、データベースに書き込まれた更新済みデータが含まれています。

Firebase では、マルチパスの更新もサポートされています。つまり、PATCH では、Firebase データベース内の複数の場所にある値を同時に更新できます。これは、データの非正規化に役立つ強力な機能です。マルチパスの更新を使用すると、Alan と Grace に同時にニックネームを追加できます。

curl -X PATCH -d '{
  "alanisawesome/nickname": "Alan The Machine",
  "gracehopper/nickname": "Amazing Grace"
}' \
  'https://docs-examples.firebaseio.com/fireblog/users.json'

この更新後は、Alan と Grace の両方にニックネームが追加されています。

{
  "users": {
    "alanisawesome": {
      "date_of_birth": "June 23, 1912",
      "full_name": "Alan Turing",
      "nickname": "Alan The Machine"
    },
    "gracehop": {
      "date_of_birth": "December 9, 1906",
      "full_name": "Grace Hopper",
      "nickname": "Amazing Grace"
    }
  }
}

パスを含めてオブジェクトを書き込むことでオブジェクトを更新すると、異なる動作が発生することに注意してください。この方法で Grace と Alan を更新するとどうなるでしょうか。

curl -X PATCH -d '{
  "alanisawesome": {"nickname": "Alan The Machine"},
  "gracehopper": {"nickname": "Amazing Grace"}
}' \
  'https://docs-examples.firebaseio.com/fireblog/users.json'

この結果、異なる動作が発生します。つまり、/fireblog/users ノード全体が上書きされます。

{
  "users": {
    "alanisawesome": {
      "nickname": "Alan The Machine"
    },
    "gracehop": {
      "nickname": "Amazing Grace"
    }
  }
}

条件付きリクエストによるデータの更新

条件付きリクエスト(REST においてトランザクションに相当)を使用すると、既存の状態に従ってデータを更新することができます。たとえば、upvote カウンタの値を増やす際、複数の upvote が同時に行われた場合にもカウントに正確に反映されるようにするには、条件付きリクエストを使用してカウンタに新しい値を書き込みます。カウンタを同じ値に変更する書き込みが 2 回行われることはなくなり、1 つの書き込みリクエストが失敗して、新しい値でリクエストを再試行できるようになります。
  1. 特定の場所で条件付きリクエストを実行するには、その場所における現在のデータの一意の ID(ETag)を取得します。その場所のデータが変更されると、ETag も変更されます。PATCH 以外の任意のメソッドを使用して ETag をリクエストできます。次の例では、GET リクエストを使用しています。
    curl -i 'https://test.example.com/posts/12345/upvotes.json' -H 'X-Firebase-ETag: true'
    
    ヘッダー内で ETag を具体的に呼び出すと、指定した場所の ETag が HTTP レスポンス内で返されます。
    HTTP/1.1 200 OK
    Content-Length: 6
    Content-Type: application/json; charset=utf-8
    Access-Control-Allow-Origin: *
    ETag: [ETAG_VALUE]
    Cache-Control: no-cache
    
    10 // Current value of the data at the specified location
    
  2. 返された ETag を次の PUT または DELETE リクエストに含めて、この ETag 値と具体的に一致するデータを更新します。この例では、カウンタを 11(最初にフェッチした値である 10 よりも 1 大きい値)に更新して、値が一致しない場合にリクエストが失敗するようにするには、次のコードを使用します。
    curl -iX PUT -d '11' 'https://[PROJECT_ID].firebaseio.com/posts/12345/upvotes.json' -H 'if-match:[ETAG_VALUE]'
    
    指定した場所にあるデータの値が 10 のままであれば、PUT リクエストの ETag が一致し、リクエストが成功すると、11 がデータベースに書き込まれます。
    HTTP/1.1 200 OK
    Content-Length: 6
    Content-Type: application/json; charset=utf-8
    Access-Control-Allow-Origin: *
    Cache-Control: no-cache
    
    11 // New value of the data at the specified location, written by the conditional request
    
    別のユーザーがデータベースに新しい値を書き込んだ場合のように、この場所が ETag と一致しなくなっていた場合、リクエストは失敗し、その場所には書き込まれません。返されたレスポンスには、新しい値と ETag が含まれます。
    HTTP/1.1 412 Precondition Failed
    Content-Length: 6
    Content-Type: application/json; charset=utf-8
    Access-Control-Allow-Origin: *
    ETag: [ETAG_VALUE]
    Cache-Control: no-cache
    
    12 // New value of the data at the specified location
    
  3. リクエストを再試行する場合は、新しい情報を使用します。Realtime Database が、失敗した条件付きリクエストを自動的に再試行することはありませんが、失敗したレスポンスによって返された情報から新しい値と ETag を取得して、新しい条件付きリクエストを作成することができます。

REST ベースの条件付きリクエストは、HTTP の if-match 標準を実装します。ただし、この標準とは次の点が異なります。

  • 各 if-match リクエストで指定できる ETag 値は 1 つのみです。ETag 値を複数指定することはできません。
  • 標準ではすべてのリクエストで ETag を返すことを推奨していますが、Realtime Database が ETag を返すのは、X-Firebase-ETag ヘッダーを含むリクエストの場合のみです。これにより、標準リクエストの費用が低くなります。

条件付きリクエストは、通常の REST リクエストよりも遅くなる可能性があります。

データのリストの保存

Firebase データベース参照に追加されたすべての子に対してタイムスタンプベースの一意のキーを生成するには、POST リクエストを送信します。users パスの場合は、各ユーザーが一意のユーザー名を持っているため、独自のキーを定義するのが合理的でした。一方、ユーザーがブログの投稿をアプリに追加したときは、POST リクエストを使用して、ブログの投稿ごとにキーを自動生成します。

curl -X POST -d '{
  "author": "alanisawesome",
  "title": "The Turing Machine"
}' 'https://docs-examples.firebaseio.com/fireblog/posts.json'

これで、posts パスには次のデータができます。

{
  "posts": {
    "-JSOpn9ZC54A4P4RoqVa": {
      "author": "alanisawesome",
      "title": "The Turing Machine"
    }
  }
}

200 OK リクエストを使用したためにキー -JSOpn9ZC54A4P4RoqVa が自動的に生成されていることに注目してください。成功したリクエストは POST HTTP ステータス コードで示されます。応答には、追加された新しいデータのキー名が含まれています。

{"name":"-JSOpn9ZC54A4P4RoqVa"}

データの削除

データをデータベースから削除するには、データの削除元にするパスの URL を含んだ DELETE リクエストを送信します。次のリクエストは、Alan を users パスから削除します。

curl -X DELETE \
  'https://docs-examples.firebaseio.com/fireblog/users/alanisawesome.json'

成功した DELETE リクエストは 200 OK HTTP ステータス コードで示されます。レスポンスには JSON null が含まれています。

URI パラメータ

REST API は、データベースにデータを書き込むときに次の URI パラメータを受け入れます。

auth

auth リクエスト パラメータを使用すると、Firebase Realtime Database セキュリティ ルールで保護されたデータにアクセスできます。このパラメータはすべての種類のリクエストでサポートされています。引数には、Firebase アプリのシークレットまたは認証トークンを使用できます。これらについては、ユーザーの承認セクションで説明します。次の例では、auth パラメータを指定して POST リクエストを送信しています。ここで、CREDENTIAL は、Firebase アプリのシークレットまたは認証トークンです。

curl -X POST -d '{"Authenticated POST request"}' \
  'https://docs-examples.firebaseio.com/auth-example.json?auth=CREDENTIAL'

print

print パラメータを使用すると、データベースからのレスポンスの形式を指定できます。リクエストに print=pretty を追加すると、データは人が読める形式で返されます。print=pretty は、リクエスト GETPUTPOSTPATCHDELETE でサポートされています。

データを書き込むときにサーバーからの出力を抑制するには、print=silent をリクエストに追加します。その結果、レスポンスは空になり、リクエストが成功した場合は 204 No Content HTTP ステータスで示されます。print=silent は、リクエスト GETPUTPOSTPATCH でサポートされています。

サーバー値の書き込み

サーバー値は、プレースホルダ値を使用して特定の場所に書き込むことができます。プレースホルダ値は、単一の ".sv" キーを持つオブジェクトです。このキーの値は、設定するサーバー値の種類です。たとえば、ユーザーが作成されたときにタイムスタンプを設定するには、次を実行します。

curl -X PUT -d '{".sv": "timestamp"}' \
  'https://docs-examples.firebaseio.com/alanisawesome/createdAt.json'

"timestamp" はサポートされている唯一のサーバー値で、UNIX エポック時刻からの経過時間です(ミリ秒単位)。

書き込みパフォーマンスの改善

大量のデータをデータベースに書き込む場合は、print=silent パラメータを使用して、書き込みパフォーマンスを改善しつつ、帯域幅の使用量を減らすことができます。通常の書き込み動作では、サーバーは、書き込まれた JSON データで応答します。print=silent が指定された場合、サーバーは、データが受信されると直ちに接続を閉じて帯域幅の使用量を減らします。

データベースに対する大量のリクエストを行う場合は、HTTP ヘッダーで Keep-Alive リクエストを送信することで HTTPS 接続を再利用できます。

エラー条件

REST API は、次の状況でエラーコードを返します。

HTTP ステータス コード
400 Bad Request(不正なリクエスト)

次のいずれかのエラー条件:

  • PUT または POST データを解析できない。
  • PUT または POST データが見つからない。
  • リクエストで PUT または POST するデータが大きすぎる。
  • REST API 呼び出しで、パスの一部に無効な子の名前が含まれている。
  • REST API 呼び出しのパスが長すぎる。
  • リクエストに、認識できないサーバー値が含まれている。
  • Firebase Realtime Database セキュリティ ルールに、クエリのインデックスが定義されていない。
  • リクエストでサポートされていないクエリ パラメータが指定されていない。
  • リクエストに、クエリ パラメータと shallow GET リクエストが混在している。
401 Unauthorized(未承認)

次のいずれかのエラー条件:

404 Not Found(未検出) 指定された Firebase データベースが見つからない。
500 Internal Server Error(内部サーバーエラー) サーバーがエラーを返した。詳細については、エラー メッセージをご覧ください。
503 Service Unavailable(サービス利用不可) 指定された Firebase Realtime Database が一時的に使用できない。この場合、リクエストは試行されていません。

データのセキュリティ保護

Firebase には、データのさまざまなノードへの読み取りと書き込みのアクセス権を持つユーザーを定義できるセキュリティ言語が備わっています。詳細については、Realtime Database セキュリティ ルールをご覧ください。

これで、データの保存についての説明は終了です。次のセクションでは、REST API を使用して Firebase データベースからデータを取得する方法について説明します。