How to Decode Base64 in Node.js (Buffers vs Browser APIs)

Published: 2023-11-12 | Category: Development

When working in a Node.js environment, handling Base64 strings is fundamentally different than in a web browser. While browsers rely on the atob() and btoa() functions, Node.js historically handled all binary data transformations through its native Buffer API.

Understanding Node.js Buffers

Node.js was designed for server-side networking and file system operations, where raw binary streams are the norm. The Buffer class allows Node to handle raw bytes efficiently outside of the V8 JavaScript engine's memory heap.

Because Base64 is a binary-to-text encoding scheme, Buffer is the perfect tool for translating between raw bytes and Base64 ASCII representations.

Basic Base64 Decoding in Node.js

To decode a Base64 string into plain text, you create a Buffer from the Base64 string, and then convert that Buffer back to a standard UTF-8 string.

JavaScript (Node.js)
// The incoming Base64 string
const base64String = 'SGVsbG8sIFdvcmxkIQ==';

// 1. Create a Buffer from the string, specifying its current encoding
const buffer = Buffer.from(base64String, 'base64');

// 2. Convert the Buffer back to a string, specifying the target encoding
const decodedText = buffer.toString('utf-8');

console.log(decodedText); 
// Output: Hello, World!

Encoding to Base64 in Node.js

Encoding follows the exact opposite pattern. You take a UTF-8 string, load it into a Buffer, and then export it specifying 'base64' as the desired format.

JavaScript (Node.js)
const originalText = 'Node.js makes Base64 easy';

// 1. Load the text into a buffer
const buffer = Buffer.from(originalText, 'utf-8');

// 2. Export as Base64
const encoded = buffer.toString('base64');

console.log(encoded);
// Output: Tm9kZS5qcyBtYWtlcyBCYXNlNjQgZWFzeQ==

Handling URL-Safe Base64 in Node.js

Node.js makes URL-safe Base64 incredibly simple. Instead of passing 'base64' to the Buffer methods, you pass 'base64url'.

This automatically handles the replacement of + with -, / with _, and strips the trailing = padding characters.

JavaScript (Node.js)
// Encoding URL-safe Base64
const urlSafeEncoded = Buffer.from('Testing URL Safe?').toString('base64url');
console.log(urlSafeEncoded); // VGVzdGluZyBVUkwgU2FmZT8

// Decoding URL-safe Base64
const decodedBuffer = Buffer.from(urlSafeEncoded, 'base64url');
console.log(decodedBuffer.toString('utf-8')); // Testing URL Safe?

Base64 and Binary Files (Images, PDFs)

If you are building an API that accepts file uploads as Base64 strings (a common pattern in JSON APIs), you must decode the string into a binary Buffer before saving it to the disk.

JavaScript (Node.js)
const fs = require('fs');

// A JSON payload containing a Base64 image
const payload = {
  filename: 'avatar.png',
  data: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=='
};

// Decode the raw base64 string to a binary buffer
const imageBuffer = Buffer.from(payload.data, 'base64');

// Write the binary buffer directly to the filesystem
fs.writeFileSync('output.png', imageBuffer);
console.log('Image saved successfully!');

The Modern Era: atob() and btoa() in Node.js

For a long time, sharing isomorphic code between the browser and Node.js was difficult because Node lacked atob() and btoa(). Developers had to use polyfills or branching logic.

Starting in Node.js v16.0.0, atob() and btoa() were added as global functions for Web API compatibility. However, the official Node.js documentation still strongly recommends using Buffer for complex operations.

Why? Because atob() and btoa() only support Latin1 characters. If you attempt to encode a string containing Emojis or UTF-8 characters (like btoa("👋")), it will crash with an InvalidCharacterError. Node.js Buffers natively handle UTF-8 without crashing.

Performance Considerations

Buffer operations in Node.js are executed in highly optimized C++ bindings. They are significantly faster than manipulating strings manually in JavaScript. When processing large Base64 payloads (like 10MB images), always rely on Buffer, and avoid using regex replacements to fix padding issues.

Conclusion

Node.js provides a robust, native solution for Base64 via the Buffer object. Whether you are generating JWT signatures, processing JSON API file uploads, or dealing with legacy systems, Buffers offer the most performant and UTF-8 safe method for binary translation.

To verify the payloads generated by your backend, you can test them directly using our free online Base64 Decoder.