How to decode a base64 encoded string returned from an api correctly

Question:

Having a base64 string encoded JSON like object coming from a python API, what is the correct way to decode and parse the encoded JSON in javascript?

Python makes it a byte string literal by adding b prefix and additional apostrophes.

I have written the following function using Buffer.from and buf.toString methods, which is working fine, but the problem is, the string I receive from the API has b'<encoded-string>' format. With the initial b and the apostrophes(').

const decodedObject: {
  foo?: string;
} = JSON.parse(
  Buffer.from(encodedString.replace("b'", '').replace("'", ''), 'base64').toString(),
);

atob and btoa seems deprecated I as understand from the warning in my IDE. That’s why I used Buffer methods

So, my question is: Removing those apostrophes and b manually didn’t feel right. Is there something I might be missing?

For example creating the encoded base64 in python:

>>> import base64
>>> "https://example.com?encoded={}".format(base64.b64encode(str({"foo":"bar"}).encode('utf-8')))
"https://example.com?encoded=b'eydmb28nOiAnYmFyJ30='"

In order to decode that base64 in javascript, I need to first remove those prefix and apostrophes.

> console.log(atob('eydmb28nOiAnYmFyJ30='))
{'foo': 'bar'}
Asked By: Berkin Anık

||

Answers:

The problem here is that your Python code is pretending strings are JSON, instead of actually using the json library to generate proper JSON.

So, let’s fix that:

import base64
import json

data = { "foo": "bar" }
encoded_value = base64.b64encode(json.dumps(data))
url = f"https://example.com?encoded={encoded_value}"

And done, the encoded URL query argument is now a normal, trivially decoded value on the JS side, because we’re guaranteed that it’s proper JSON, just base64 encoded.

Which means that in order to unpack that on the JS side, we just run through the obvious steps:

  1. get the query value,
  2. decode it,
  3. parse it as JSON

So:

// I have no idea if you're running Node, a browser, Deno, etc.
// so substitute window.location or your framework's path object.
const url = new URL(request.path)

// get the encoded value
const query = new URLSearchParams(url.search)
const encoded = query.get(`encoded`)

// turn it back into a JSON string and parse that
const decoded = base64Decode(encoded); // again, no idea where you're running this.
try {
  const unpacked = JSON.parse(decoded);
  console.log(unpacked); // { foo: "bar" }
} catch (e) {
  // ALWAYS run JSON.parse in a try/catch because it can, and will, throw.
}

Noting that that base64Decode(encoded) is a stand-in for whatever base64 library you’re using.