Explain Codes LogoExplain Codes Logo

Query-string encoding of a JavaScript object

javascript
prompt-engineering
functions
recursion
Nikita BarsukovbyNikita Barsukov·Oct 4, 2024
TLDR

For quick and easy query-string encoding of a JavaScript object, serialize the object using encodeURIComponent to ensure it's properly URL encoded:

const obj = { name: 'Stack', language: 'JavaScript' }; const queryString = Object.entries(obj) .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`) .join('&'); console.log(queryString);

This one-liner creates a URL safe query-string by pairing keys with values.

Encoding complex objects

This method works well for flat JavaScript objects. However, when dealing with nested or complex objects, additional steps are needed to handle nested properties or arrays.

In this case, we create a serialize function which makes use of recursion to walk the object's properties, creating a properly encoded query-string:

function serialize(obj, prefix) { const queryString = []; for (let key in obj) { if (!obj.hasOwnProperty(key)) continue; // We skip those keys which are not object's direct property const fullKey = prefix ? `${prefix}[${encodeURIComponent(key)}]` // Forget inception, even Leonardo DiCaprio can't decode this : encodeURIComponent(key); const value = obj[key]; queryString.push( (value !== null && typeof value === "object") // "To be or not to be, that is the question" - Shakespeare while encoding ? serialize(value, fullKey) : `${fullKey}=${encodeURIComponent(String(value))}` ); } return queryString.join('&'); }

URLSearchParams: Upgrade your code

For a more advanced solution, URLSearchParams is a modern API that simplifies preparing a query-string. It automatically encodes the elements and is highly recommended where support is not an issue.

const params = new URLSearchParams(); const data = { user: 'Coder123', skills: ['JS', 'Python', 'Java'] }; Object.keys(data).forEach(key => { const value = data[key]; if (Array.isArray(value)) { // Always check for arrays: Attack of the Clones didn't end well. value.forEach(item => params.append(key, item)); } else { params.set(key, value); } }); console.log(params.toString()); // "Say hello to my encoded friend!" - Tony Montana

Encoding corner cases

To address special cases and efficiently encode complex objects, it's beneficial to have more flexible solutions like using Object.entries() which allows for handling both simple objects and complex structures, let's see how:

const encodeSimple = obj => Object.entries(obj) .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`) .join('&'); // "Just because it's simple, doesn't mean it's not a genius!" - Albert Einstein probably. const encodeComplex = (obj, parentKey = '') => { const pairs = []; for (let [key, value] of Object.entries(obj)) { if (value instanceof Object) { pairs.push(...encodeComplex(value, parentKey ? `${parentKey}[${key}]` : key)); } else { pairs.push(`${parentKey ? `${parentKey}[${encodeURIComponent(key)}]` : encodeURIComponent(key)}=${encodeURIComponent(value)}`); } } return pairs.join('&'); // "It's not over until it's over" - Yogi Berra about nested objects. };

Dealing with potential issues

Special characters and encoding

Say you have special characters in values. Using encodeURIComponent ensures they are properly encoded:

const complexVal = { query: 'Complex? &="Yes"' }; console.log(`Complex encoding: ${encodeSimple(complexVal)}`);

This will encode them so they don't disrupt the query-string.

Encoding dates and other non-string values

Objects may contain non-string values like Date objects, which must be converted to strings before encoding:

const paramsWithDate = { event: 'Conference', date: new Date() }; console.log(`Date encoding: ${encodeSimple(paramsWithDate)}`);

This method converts the date to an UTC string and encodes it.

Reusability and practicality

When scaling to enterprise level, encapsulating the query-string encoding functionality in a separate module encourages code reuse and simplifies maintenance:

export const queryStringEncoder = { encode: encodeSimple, encodeComplex: encodeComplex };

This way, anyone can import these functions and use them across projects.

References