How to Use Multi-Path Update with the Rest API in Firebase? Error 400

Firebase multi-path update invalid data; couldn't parse JSON object, array, or value

After an unsuccessful attempt at using the official support@firebase.com channel, I took to brute force debugging.

I parsed out each key/value pair separately as its own table and attempted a multi-path update, and each time it worked. This is when I knew I was onto something odd. Then I slowly built up the entire table key by key until the multi-path update failed and I saw the issue.

My update contained the following:

{
"foo/bar": {
"data": {
"baz": 1
}
},

"foo/bar/data": {
"quu": 2
}
}

And I was hoping for the resulting data in Firebase:

{
foo: {
bar: {
data: {
baz: 1,
quu: 2
}
}
}
}

So, the simple answer is, a multi-path update cannot contain two key names that write to the same location (or a location deeper in the same path).

Now, my multi-path update contained upwards of 20 key/value pairs, so it wasn't quite as easy to spot as the example I've laid out here, so cut me a little slack. I can understand for a number of reasons why this may not be allowed (the atomicity of the request, which update gets applied first, etc), but my issue is that the error returned from Firebase was not only not helpful, it flat out pointed me in the wrong direction, making debugging even harder.

So, the answer is to combine the two multi-path update keys that write to the same location in Firebase to look like the following:

{
"foo/bar/data" : {
"baz": 1,
"quu": 2
}
}

Does Firebase Realtime Database REST API support multi path updates at different entity locations?

If the updates are going to a single database, there is always a common path.

In your case you'll run the PATCH command against the root of the database:

curl -X PATCH -d '{
"path1/17": json1,
"path2/1733455": json2
}' 'https://yourdatabase.firebaseio.com/.json'

The key difference with your URL seems to be the / before .json. Without that you're trying to connect to a domain on the json TLD, which doesn't exist (yet) afaik.

Note that the documentation link you provide for Batched Updates is for Cloud Firestore, which is a completely separate database from the Firebase Realtime Database.

Firebase: How to update multiple nodes transactionally? Swift 3

In the Firebase documentation in the section Enable Offline Capabilities it is specified that:

Transactions are not persisted across app restarts

Even with persistence enabled, transactions are not persisted across
app restarts.

So you cannot rely on transactions done offline being
committed to your Firebase Realtime Database.

Therefore:

1. there is no way to use firebase transactions at client side to update a value at two or more paths.

2. using a completion callback to perform the second write is not viable since the client could restart the app before a response is received in the completion handler from the firebase server, thus leaving the database in inconsistent state.

I assume that my only option to update data transactionally at the first path and further update the second path with that data that was already written at the first path in Firebase Database, would be to use Conditional Requests over REST as specified in the Firebase Documentation.

This would achieve the same functionality provided by Firebase framework for IOS clients.

    1. The client will make a request via Alamofire to my server (I will use Vapor framework so as to leverage the Swift language), once the request is received at the Vapor server, a GET request will be sent to the Firebase Database server root/users/bookings/4875383 in which I will request an ETAG_VALUE

What is ETAG Value?: ( a unique identifier which will be different every time data changes at the path where GET Request is made. Namely if another user writes to the same path before my write request the resource succeeds, my write operation will be rejected since the ETAG value at the path will have already been modified by the other user's write operation. This would enable users to write data transactionally to a path)


    1. a response is received from the Firebase Server containing a an ETAG_VALUE.

    1. make a PUT request to the Firebase Server and in the header specify the ETag: [ETAG_VALUE] received from the previous GET request. If the ETAG value posted to the server matches the value at the Firebase Server, the write will succeed. 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.

    1. Furthermore, now we can update the value at root/Cleaners/bookings/4875383 to reflect the job that was claimed by a cleaner.

Use transaction to update value at two different nodes

You can push all your logic for updating the database onto the server side with Cloud Functions for Firebase. Use can use a database trigger to respond to data being written in the database, then execute some JavaScript to make sure the fan-out finishes correctly. It will have the advantage of making sure all the changes happen without depending on the client.

Transactions can't modify data at two different locations at once, but you will still probably want to use them in your client and Cloud Functions to make sure concurrent writes will not have problems.

firebase POST url 400 bad request

There are a number of things wrong here:

  • You aren't using the value of id or num here, you need to wrap them in square brackets
  • The body property of a fetch API call should be passed through JSON.stringify.
  • You shouldn't post a Date object in the body of a request. Either convert it to a string, convert it to a plain number, use a timestamp object or use a server value placeholder.
  • Your code always assumes that the post was created successfully. The fetch API will not throw an error for bad status codes. You should check the status code to determine what you want to do. As Firebase Database operations usually respond in JSON, I've parsed the body here as JSON before checking the code however you should probably check for empty bodies first.
  • Using POST /blogs.json will overwrite your entire /blogs tree each time you write data. You should change the path or use a PATCH request with the proper body for it.
const id = localStorage.getItem('uid')
const postData = async () => {
const num = localStorage.getItem('blogNumber')
const reqData = {
[id]: { // <-- use square brackets for value as key
[num]: { // <-- use square brackets for value as key
title: titleRef.current.value,
description: contentRef.current.value,
createdAt: Date.now(), // <-- returns milliseconds since epoch
isPosted: false
}
}
}

const resp = await fetch(
'https://assignment-c3557-default-rtdb.asia-southeast1.firebasedatabase.app.firebaseio.com/blogs.json',
{
method: 'POST',
body: JSON.stringify(reqData), // you need to stringify this yourself
headers: {
'Content-Type': 'application/json'
}
}
);


const data = await resp.json();

if (!resp.ok) { // fetch API does not throw errors by itself
const err = new Error('Unexpected status: ' + resp.status);
err.data = data;
err.resp = resp;
throw err;
}

localStorage.setItem('blogNumber', localStorage.getItem('blogNumber')+1)
}

Updated to use deeper path:

const id = localStorage.getItem('uid')
const postData = async () => {
const num = localStorage.getItem('blogNumber')
const reqData = {
title: titleRef.current.value,
description: contentRef.current.value,
createdAt: Date.now(), // <-- returns milliseconds since epoch
isPosted: false
}

const resp = await fetch(
`https://assignment-c3557-default-rtdb.asia-southeast1.firebasedatabase.app.firebaseio.com/blogs/${uid}/${num}.json`,
{
method: 'POST',
body: JSON.stringify(reqData), // you need to stringify this yourself
headers: {
'Content-Type': 'application/json'
}
}
);

const data = await resp.json();

if (!resp.ok) { // fetch API does not throw errors by itself
const err = new Error('Unexpected status: ' + resp.status);
err.data = data;
err.response = resp;
throw err;
}

localStorage.setItem('blogNumber', localStorage.getItem('blogNumber')+1)
}

How to investigate errors in Firebase Remote Config API?

Google Developer Support pointed me to a known bug in the Firebase Remote Config library which has been fixed recently. Upgrading the library indeed fixed the issue.



Related Topics



Leave a reply



Submit