Ways to Save Data
|
PUT
|
Write or replace data to a
defined path
, like
fireblog/users/user1/<data>
|
PATCH
|
Update some of the keys for a defined path without replacing all of the data.
|
POST
|
Add to a list of data
in our Firebase database. Every time we send a
POST
request, the Firebase client generates a unique key, like
fireblog/users/<unique-id>/<data>
|
DELETE
|
Remove data from the specified Firebase database reference.
|
Writing Data with PUT
The basic write operation through the REST API is
PUT
. To
demonstrate saving data, we'll build a blogging application with posts and users. All of the
data for our application will be stored under the path of `fireblog`, at the Firebase database URL
`https://docs-examples.firebaseio.com/fireblog`.
Let's start by saving some user data to our Firebase database. We'll store each user by a unique
username, and we'll also store their full name and date of birth. Since each user will have a
unique username, it makes sense to use
PUT
here instead of
POST
since
we already have the key and don't need to create one.
Using
PUT
, we can write a string, number, boolean, array or any JSON object to
our Firebase database. In this case we'll pass it an object:
curl -X PUT -d '{
"alanisawesome": {
"name": "Alan Turing",
"birthday": "June 23, 1912"
}
}' 'https://docs-examples.firebaseio.com/fireblog/users.json'
When a JSON object is saved to the database, the object properties are
automatically mapped to child locations in a nested fashion. If we navigate
to the newly created node, we'll see the value "Alan Turing". We can also
save data directly to a
child location:
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'
The above two examples—writing the value at the same time as an object and writing them
separately to child locations—will result in the same data being saved to our Firebase
database:
{
"users": {
"alanisawesome": {
"date_of_birth": "June 23, 1912",
"full_name": "Alan Turing"
}
}
}
A successful request will be indicated by a
200 OK
HTTP status code, and the
response will contain the data we wrote to the database. The first example will only
trigger one event on clients that are watching the data, whereas the second example will trigger
two. It is important to note that if data already existed at the users path, the first approach
would overwrite it, but the second method would only modify the value of each separate child
node while leaving other children unchanged.
PUT
is equivalent to
set()
in our JavaScript SDK.
Updating Data with PATCH
Using a
PATCH
request, we can update specific children at a location without
overwriting existing data. Let's add Turing's nickname to his user data with a
PATCH
request:
curl -X PATCH -d '{
"nickname": "Alan The Machine"
}' \
'https://docs-examples.firebaseio.com/fireblog/users/alanisawesome.json'
The above request will write
nickname
to our
alanisawesome
object
without deleting the
name
or
birthday
children. Note that if we had
issued a
PUT
request here instead,
name
and
birthday
would have been deleted since they were not included in the request. The data in our Firebase
database now looks like this:
{
"users": {
"alanisawesome": {
"date_of_birth": "June 23, 1912",
"full_name": "Alan Turing",
"nickname": "Alan The Machine"
}
}
}
A successful request will be indicated by a
200 OK
HTTP status code, and the
response will contain the updated data written to the database.
Firebase also supports multi-path updates. This means that
PATCH
can now update values at multiple locations in your Firebase database at the same time, a powerful feature which allows helps you
denormalize your data
. Using multi-path updates, we can add nicknames to both Alan and Grace at the same time:
curl -X PATCH -d '{
"alanisawesome/nickname": "Alan The Machine",
"gracehopper/nickname": "Amazing Grace"
}' \
'https://docs-examples.firebaseio.com/fireblog/users.json'
After this update, both Alan and Grace have had their nicknames added:
{
"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"
}
}
}
Note that trying to update objects by writing objects with the paths included will result in different behavior. Let's take a look at what happens if we instead try to update Grace and Alan this way:
curl -X PATCH -d '{
"alanisawesome": {"nickname": "Alan The Machine"},
"gracehopper": {"nickname": "Amazing Grace"}
}' \
'https://docs-examples.firebaseio.com/fireblog/users.json'
This results in different behavior, namely overwriting the entire
/fireblog/users
node:
{
"users": {
"alanisawesome": {
"nickname": "Alan The Machine"
},
"gracehop": {
"nickname": "Amazing Grace"
}
}
}
Updating Data with Conditional Requests
You can use conditional requests, the REST equivalent to transactions, to update
data according to its existing state. For
example, if you want to increase an upvote counter, and want to make sure the
count accurately reflects multiple, simultaneous upvotes, use a conditional
request to write the new value to the counter. Instead of two writes
that change the counter to the same number, one of the write requests fails and
you can then retry the request with the new value.
- To perform a conditional request at a location, get the unique identifier
for the current data at that location, or the ETag. If the data changes at
that location, the ETag changes, too. You can request an ETag with any
method other than
PATCH
. The following example uses a
GET
request.
curl -i 'https://test.example.com/posts/12345/upvotes.json' -H 'X-Firebase-ETag: true'
Specifically calling the ETag in the header returns the ETag of the
specified location in the HTTP response.
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
- Include the returned ETag in your next
PUT
or
DELETE
request to update
data that specifically matches that ETag value. Following our example, to
update the counter to 11, or 1 larger than the initial fetched value of 10,
and fail the request if the value no longer matches, use the following code:
curl -iX PUT -d '11' 'https://
[PROJECT_ID]
.firebaseio.com/posts/12345/upvotes.json' -H 'if-match:
[ETAG_VALUE]
'
If the value of the data at the specified location is still 10, the ETag in the
PUT
request matches, and the request succeeds, writing 11 to the database.
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
If the location no longer matches the ETag, which might occur if another user
wrote a new value to the database, the request fails without writing to the
location. The return response includes the new value and 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
- Use the new information if you decide to retry the request. Realtime Database
does not automatically retry conditional requests that have failed. However,
you can use the new value and ETag to build a new conditional request with
the information returned by the fail response.
REST-based conditional requests implement the HTTP
if-match
standard. However, they differ from the standard in the following ways:
- You can only supply one ETag value for each if-match request, not multiple.
- While the standard suggests that ETags be returned with all requests,
Realtime Database only returns ETags with requests including the
X-Firebase-ETag
header. This reduces billing costs for standard requests.
Conditional requests might also be slower than typical REST requests.
Saving Lists of Data
To generate a unique, timestamp-based key for every child added to a Firebase database reference
we can send a
POST
request. For our
users
path, it made sense to
define our own keys since each user has a unique username. But when users add blog posts to the
app, we'll use a
POST
request to auto-generate a key for each blog post:
curl -X POST -d '{
"author": "alanisawesome",
"title": "The Turing Machine"
}' 'https://docs-examples.firebaseio.com/fireblog/posts.json'
Our
posts
path now has the following data:
{
"posts": {
"-JSOpn9ZC54A4P4RoqVa": {
"author": "alanisawesome",
"title": "The Turing Machine"
}
}
}
Notice that the key
-JSOpn9ZC54A4P4RoqVa
was automatically generated for us because
we used a
POST
request. A successful request will be indicated by a
200 OK
HTTP status code, and the response will contain the key of the new data that was added:
{"name":"-JSOpn9ZC54A4P4RoqVa"}
Removing Data
To remove data from the database, we can send a
DELETE
request with the
URL of the path from which we'd like to delete data. The following would delete Alan from our
users
path:
curl -X DELETE \
'https://docs-examples.firebaseio.com/fireblog/users/alanisawesome.json'
A successful
DELETE
request will be indicated by a
200 OK
HTTP status code with a response containing JSON
null
.
URI Parameters
The REST API accepts the following URI parameters when writing data to the database:
auth
The
auth
request parameter allows access to data protected by
Firebase Realtime Database Security Rules
, and is
supported by all request types. The argument can either be our Firebase app secret or an
authentication token, which we'll cover in the
user authorization
section
. In the following example we send a
POST
request with an
auth
parameter, where
CREDENTIAL
is either our Firebase app secret or
an authentication token:
curl -X POST -d '{"Authenticated POST request"}' \
'https://docs-examples.firebaseio.com/auth-example.json?auth=CREDENTIAL'
print
The
print
parameter lets us specify the format of our response from the
database. Adding
print=pretty
to our request will return the data in a
human-readable format.
print=pretty
is supported by
GET
,
PUT
,
POST
,
PATCH
, and
DELETE
requests.
To suppress the output from the server when writing data, we can add
print=silent
to our request. The resulting response will be empty and indicated by
a
204 No Content
HTTP status code if the request is successful.
The
print=silent
is supported by
GET
,
PUT
,
POST
, and
PATCH
requests.
Writing Server Values
Server values can be written at a location using a placeholder value, which is an object with a
single
".sv"
key. The value for that key is the type of server value we wish to set.
For example, to set a timestamp when a user is created we could do the following:
curl -X PUT -d '{".sv": "timestamp"}' \
'https://docs-examples.firebaseio.com/alanisawesome/createdAt.json'
"timestamp"
is the only supported server value, and is the time since the UNIX
epoch in milliseconds.
If we're writing large amounts of data to the database, we can use the
print=silent
parameter to improve our write performance and decrease bandwidth
usage. In the normal write behavior, the server responds with the JSON data that was written.
When
print=silent
is specified, the server immediately
closes the connection once the data is received, reducing bandwidth usage.
In cases where we're making many requests to the database, we can re-use the HTTPS
connection by sending a
Keep-Alive
request in the HTTP header.
Error Conditions
The REST API will return error codes under these circumstances:
HTTP Status Codes
|
400
Bad Request
|
One of the following error conditions:
- Unable to parse
PUT
or
POST
data.
- Missing
PUT
or
POST
data.
- The request attempts to
PUT
or
POST
data that
is too large.
- The REST API call contains invalid child names as part of the path.
- The REST API call path is too long.
- The request contains an unrecognized server value.
- The index for the query is not defined in your
Firebase Realtime Database Security Rules
.
- The request does not support one of the query parameters that is specified.
- The request mixes query parameters with a shallow
GET
request.
|
401
Unauthorized
|
One of the following error conditions:
|
404
Not Found
|
The specified Firebase database was not found.
|
500
Internal Server Error
|
The server returned an error. See the error message for further details.
|
503
Service Unavailable
|
The specified Firebase Realtime Database is temporarily unavailable, which means the
request was not attempted.
|
Securing Data
Firebase has a security language that lets us define which users have read and write access to
different nodes of our data. You can read more about it in
Realtime Database Security Rules
.
Now that we've covered saving data, we can learn how to retrieve our data from the Firebase
database through the REST API in the next section.