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 aGET
request. Specifically calling the ETag in the header returns the ETag of the specified location in the HTTP response.curl -i 'https://test.example.com/posts/12345/upvotes.json' -H 'X-Firebase-ETag: true'
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
orDELETE
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: If the value of the data at the specified location is still 10, the ETag in thecurl -iX PUT -d '11' 'https://[PROJECT_ID].firebaseio.com/posts/12345/upvotes.json' -H 'if-match:[ETAG_VALUE]'
PUT
request matches, and the request succeeds, writing 11 to the database. 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 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
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'
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.
Improving Write Performance
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:
|
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.