Cleaner way of dealing with query params using URLSearchParams

20 Aug 2021

Ever had a situation where you are supposed to convert an object (key, values) into a query parameter and build a URL around it?

Here's an example:

let user = {
  name: 'John Doe',
  age: 24,
}

Let's say the URL that we want to build / API requires the fields of the above object to be as ?name=..&age=...

The most straightforward way of doing this would be looping through keys of the object and manually appending the key/values to a String while separating each value with an &. Oh and don't forget to URL encode each value using encodeURIComponent.

For example: John Doe should be encoded as John%20Doe or John+Doe.

let obj = {
  name: 'John Doe',
  age: 24,
}

function buildQuery(obj) {
  let query = ''
  Object.entries(obj).forEach(([key, val], currentIndex, entries) => {
    let keyValPair = `${key}=${encodeURIComponent(val)}`
    if (currentIndex !== entries.length - 1) {
      keyValPair += '&' // PS: Don't want URLs ending with '&' :)
    }
    query += keyValPair
  })
  return query
}

buildQuery(obj)
// Output: "name=John%20Doe&age=20"

You can implement the same with reduce too:

function buildQuery(obj) {
  return Object.entries(obj).reduce(
    (query, [key, val], currentIndex, entries) => {
      let keyValPair = query + `${key}=${encodeURIComponent(val)}`
      if (currentIndex !== entries.length - 1) {
        keyValPair += '&' // PS: Don't want URLs ending with '&' :)
      }
      return keyValPair
    },
    ''
  )
}

// Output: "name=John%20Doe&age=20"

There are other ways of doing this which are more or less similar. But in my opinion, there's a much cleaner way of dealing with this problem. If you haven't read the title yet, it is by using URLSearchParams

function buildQuery(obj) {
  let query = new URLSearchParams()
  Object.entries(obj).forEach(([key, val]) => {
    query.set(key, val)
  })
  return query.toString()
}

// Output: "name=John+Doe&age=20"

The set(key,val) method in the URLSearchParams class sets/replaces the key and value into the query whereas append(key, val) appends/adds the key/value into the query. You can use either of those based on the use case;

What about the other way round? What if I have a URL and I want to build an object out of the key/values you ask?

Yep, URLSearchParams has you covered. You can initialize the URLSearchParams constructor with a string i.e query params and loop over it using either forEach loop or the classic for..of loop.

let query = 'name=John%Doe&age=20'

function buildUser(query) {
  let params = new URLSearchParams(query)
  let user = {}
  params.forEach((val, key) => {
    user[key] = val
  })
  return user
}

// Output: {name: "John Doe", age: "20"}

URLSearchParams also comes with some handy utility functions like has, get, values etc. I encourage you to look it up and read more about it.

Further Reading / References: