713

Is there a fast and simple way to encode a JavaScript object into a string that I can pass via a GET request?

No jQuery, no other frameworks—just plain JavaScript :)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
napolux
  • 15,574
  • 9
  • 51
  • 70
  • 1
    Why can't JQuery be a solution if there is an appropriate one for your solution? – eaglei22 Mar 30 '17 at 22:38
  • 5
    @eaglei22 because at the time I was working on a project for an IPTV set top box device and no external libraries were allowed. ;-) – napolux Mar 31 '17 at 08:22
  • 1
    Thanks for the response. I see this specification from time to time and always wondered a scenario why. Well, now I got one, thanks! :) – eaglei22 Mar 31 '17 at 13:48
  • 22
    @eaglei22 Because sometimes you don't want to load a large library to get one element by id. – Aaron Butacov Jun 26 '17 at 20:46
  • most browsers support `URLSearchParams` now... – mb21 Mar 12 '18 at 17:11
  • I made a website for converting JSON to HTTPs Query: https://kshitijdhyani.com/JSONtoHTTPSerializer/ Hope it helps. – Kshitij Dhyani Feb 26 '22 at 18:33
  • Does this answer your question? [How to create query parameters in Javascript?](https://stackoverflow.com/questions/111529/how-to-create-query-parameters-in-javascript) – Dan Dascalescu Oct 13 '22 at 08:19
  • When it comes for Array URL encoding, I found [this article](https://www.slingacademy.com/article/how-to-pass-a-javascript-array-within-a-query-string/) was worth sharing! – artu-hnrq May 19 '23 at 18:31

48 Answers48

973

Like this:

serialize = function(obj) {
  var str = [];
  for (var p in obj)
    if (obj.hasOwnProperty(p)) {
      str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
    }
  return str.join("&");
}

console.log(serialize({
  foo: "hi there",
  bar: "100%"
}));
// foo=hi%20there&bar=100%25

This one also converts recursive objects (using PHP "array" notation for the query string):

serialize = function(obj, prefix) {
  var str = [],
    p;
  for (p in obj) {
    if (obj.hasOwnProperty(p)) {
      var k = prefix ? prefix + "[" + p + "]" : p,
        v = obj[p];
      str.push((v !== null && typeof v === "object") ?
        serialize(v, k) :
        encodeURIComponent(k) + "=" + encodeURIComponent(v));
    }
  }
  return str.join("&");
}

console.log(serialize({
  foo: "hi there",
  bar: {
    blah: 123,
    quux: [1, 2, 3]
  }
}));
// foo=hi%20there&bar%5Bblah%5D=123&bar%5Bquux%5D%5B0%5D=1&bar%5Bquux%5D%5B1%5D=2&bar%5Bquux%5D%5B2%5D=3
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user187291
  • 53,363
  • 19
  • 95
  • 127
  • 2
    Won't it break given {foo: [1,2,3], bar: "100%" } ? – Quentin Nov 11 '09 at 12:53
  • If the values in your associative array can be arrays/objects themselves, then this won't work (because encodeURIComponent is expecting a string - i hope it works with numbers...). You have to somehow represent the fact that this value has "sub-values" and so on. smart people have already thought about this problem and came up with JSON, so making up your own representation is reinventing the wheel. that's why I think in general my answer is better (although it may be that in your specific case this answer is good enough). – Ofri Raviv Nov 11 '09 at 12:54
  • 2
    @Ofri: For POST requests to a server set up to receive it, JSON is a good choice. For GET requests, if you're sending anything other than a few simple parameters to the server then it's likely your design is wrong. – Tim Down Nov 11 '09 at 14:25
  • Hi I did not bring the second example to work. See my fiddle http://jsfiddle.net/suterma/U5Tyb/ which is a bare copy of the source code. JSLint complains and the result is just a large mess. I think there is a problem with recursion. – Marcel Sep 12 '12 at 20:33
  • 2
    @Marcel That's because the function doesn't check for hasOwnProperty. I've updated your fiddle so now it does: http://jsfiddle.net/rudiedirkx/U5Tyb/1/ – Rudie Jan 05 '13 at 18:22
  • I'm just wondering - is this in any way significantly different from jQuery's param function? I just want to ensure both do the same thing. The serialize function defined here writes %5D%5B and then the index number, then %5D. What is this difference? $.param({bar: { quux: ['a', 'b', 'c'] }}); "bar%5Bquux%5D%5B%5D=a&bar%5Bquux%5D%5B%5D=b&bar%5Bquux%5D%5B%5D=c" serialize({bar: { quux: ['a', 'b', 'c'] }}); "bar%5Bquux%5D%5B0%5D=a&bar%5Bquux%5D%5B1%5D=b&bar%5Bquux%5D%5B2%5D=c" – dchang Jan 30 '13 at 19:21
  • @dchang it's a bit easier to see what's going on if you use the decoded strings: `jQuery: "bar[quux][]=a&bar[quux][]=b&bar[quux][]=c"` `serialize: "bar[quux][0]=a&bar[quux][1]=b&bar[quux][2]=c"` In practice, the jQuery version is "more correct" in that it correctly represents an array in query syntax, as I understand it. The serialize string is actually represents an associative array with numeric keys. – Chris Hall Dec 08 '14 at 20:12
  • the second script has a bug though. When passing an empty array, it wont be appended at all, as in: `serialize({foo: [] });` The function loops again because it is counted as an Object, thus skipping the for-loop because its empty. IMO when you pass something like this, it should be appended like '&foo=' (without a value) or '&foo[]=' – Flame May 06 '15 at 11:51
  • 1
    @TimDown Regarding your comment sending simple parameters in GET requests. I do not agree. Grouping parameters into arrays might turn handy as PHP in the server side finds a ready-steady associative array to go. I can't see why this is wrong as a design. – Savas Vedova Nov 27 '15 at 10:15
  • @SavasVedova: The page URL is just an address pointing to a resource. In general, the simpler this address is, the better: a shorter, simpler URL is easier to deal with for the user and at least used to be favoured by search engines. There's also a relatively small limit on the length of a URL (of the order of a couple of kilobytes, I think) which varies between browsers. – Tim Down Nov 27 '15 at 11:07
  • 2
    Is the 'if (obj.hasOwnProperty(prop))' necessary? The for in statement loop just over the properties of the object so calling hasOwnProperty always evaluate to true – Arnon May 26 '16 at 12:07
  • 1
    on aspnet mvc you could use `var k = prefix ? prefix + '.' + p : p, v = obj[p];` so the model binding works! thanks –  Sep 28 '16 at 20:45
  • @Flame It's not a bug. You can only send strings. `&foo[]=` would result in an array containing an empty string. Another solution (if you dislike not sending anything) would be converting the array to JSON, but then you'd have to parse in on the server... – pishpish Apr 20 '17 at 11:06
  • In order to avoid serialization of null values, I had to change the first if condition (after the for) like this : if (obj.hasOwnProperty(p) && obj[p]) – sscarduzio Mar 15 '18 at 21:15
  • How to test the correctness of it? Anyone has deserialize function of it. Please help. – Mahesh Bongani Oct 12 '20 at 19:16
  • Why couldn't you just use real variable names? -1, not a clean code. – AMD Jan 28 '21 at 16:04
  • I prefer commas when serializing **arrays** (instead of repeating the key). [See here](https://www.npmjs.com/package/query-string#user-content-arrayformat) comma-version which is much shorter hence readable – vsync Feb 05 '21 at 11:03
  • @Arnon It helps to avoid edge cases like this: `{ key1:undefined, null: 'val2' }`. Omitting the `hasOwnProperty` check will cause those keys to be included as: `key1=undefined&null=val2` – Tamir Nakar Mar 07 '21 at 14:20
  • The `query-string` library works well for this. The `querystring` library does not correctly encode nested objects – James Klein Aug 14 '21 at 00:29
  • Definitely the best was of doing this. If needed I would also add object grouping so its a little clearer when decoding – Tim Bogdanov Oct 06 '21 at 16:52
  • 1
    To avoid **ESLint error on rule "no-prototype-builtins"**, you should have to refactor your function so that instead of `obj.hasOwnProperty(p)` it could be, `Object.prototype.hasOwnProperty.call(obj, p)` or `{}.propertyIsEnumerable.call(obj, p)` or `!!Object.getOwnPropertyDescriptor(obj, p)` – Luis Martin Jan 13 '22 at 07:36
727

Just use URLSearchParams This works in all current browsers

new URLSearchParams(object).toString()
mirucon
  • 177
  • 1
  • 1
  • 10
HumeNi
  • 7,788
  • 1
  • 11
  • 9
  • 28
    No as it does not do recursive objects – Eddie Monge Jr Apr 19 '19 at 00:44
  • 126
    @EddieMongeJr Query strings are key-value pairs by design, you shouldn't even want to serialize nested objects This answer is the modern way to go. Upvotes needed. – Romain Durand Aug 06 '19 at 15:12
  • 17
    Yes they are key value pairs but there is nothing that says that the value can't be a string encoded object. Also, the original questions asks for a "Javascript Object into a string", which can have nested properties – Eddie Monge Jr Aug 07 '19 at 16:38
  • 4
    @EddieMongeJr even the accepted answer (and the others after a briefly look) doesn't support nested object. You can `stringify` the nested objects before you can to `URLSearchParams` – Mosh Feu Jan 13 '20 at 06:30
  • 3
    it doesn't accept objects as parameters, only strings – bokkie Jun 19 '20 at 16:26
  • @EddieMongeJr `GET` requests are not for complex/nested objects. That's what other methods are for, like `POST`, where you can set the request payload to any format you want (e.g JSON). URLs are not for that. – gilad905 Feb 19 '21 at 17:24
  • 1
    @bokkie it accepts Object indeed https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/URLSearchParams – Joel Filipe Proença Rodrigues Mar 02 '21 at 18:07
  • 1
    @JoelFilipeProençaRodrigues, the link says `Record`. That is not "only strings", but it is not a generic object either. Luckily, browsers are helpful and convert other property types to strings for us, although not required by spec. It will not work without a cast in TypeScript: `new URLSearchParams(object as any)`. – Mattias Wallin Apr 23 '21 at 13:46
  • 2
    only if one is ok with encoding space as +. many backend solutions break with this one – bugwheels94 Jul 30 '21 at 02:10
  • 1
    Does not work with Meteor.js-based APIs that consume a `sort: { value: 1, _id: -1 }` style search param syntax. – Reverse Engineered Nov 24 '21 at 08:37
251

jQuery has a function for this, jQuery.param(). If you're already using it, you can use this:

Example:

var params = { width:1680, height:1050 };
var str = jQuery.param( params );

str now contains width=1680&height=1050.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
benvds
  • 3,690
  • 1
  • 17
  • 14
  • 160
    quoting Napolux (the OP): _"just plain Javascript"_. :P – Sk8erPeter Jul 18 '12 at 15:53
  • 6
    jQuery.param() has sinister behavior. Try to execute var a = []; a[2564] = 12; console.log(jQuery.param({ propertylist: a })); to see what I mean. – akond Aug 14 '13 at 12:32
  • 16
    @akond The jQuery documentation specifically says that you may not pass in a bare array. – Ariel Jun 26 '14 at 20:47
  • 4
    @Ariel He's not passing in a bare array. He's passing in an array with only one value at index 2564. To demonstrate: `var a = []; a[5] = 'foo'; jQuery.param({ parameters: a });` Results in `"parameters[]=&parameters[]=&parameters[]=&parameters[]=&parameters[]=&parameters[]=foo"`. Which, while correct, may not be what you expect. – Chris Hall Dec 08 '14 at 20:17
  • @Markus That's what a bare array *IS*. What do *you* think a bare array is? Are you confusing it with an empty array? – Ariel Dec 09 '14 at 06:47
  • @Ariel I'd never heard the term before, so, yes, I was. Further, I just checked the jQuery documentation for `$.param` and find no mention of bare arrays or any mention of this potential gotcha. – Chris Hall Dec 09 '14 at 17:59
  • @Markus "If the object passed is in an Array, it must be an array of objects in the format returned by .serializeArray()". I don't remember if it used to have the term "bare array" 3 years ago, or if I called it that. – Ariel Dec 10 '14 at 00:09
  • 1
    @Ariel that line is only valid if the argument to `param` is an array. Passing in an object that contains an array does not invoke any of those rules. – Chris Hall Dec 11 '14 at 17:52
  • @Markus You are being overly literal. But I guess you can file a bug with them to clarify the documentation language. – Ariel Dec 11 '14 at 23:23
  • @Ariel I'm not being overly literal. If you don't pass an array in as the only argument, that rule is not applied at all. Ex: `$.param({ param: [ { name: "a", value: "1" }, { name: "b", value: "2" }, { name: "c", value: "3" }, { name: "d", value: "4" }, { name: "e", value: "5" } ]})` Results in the following (decoded) query string: `"param[0][name]=a&param[0][value]=1&param[1][name]=b&param[1][value]=2&param[2][name]=c&param[2][value]=3&param[3][name]=d&param[3][value]=4&param[4][name]=e&param[4][value]=5"` – Chris Hall Dec 16 '14 at 18:18
  • @Ariel Based on my understanding of what you're saying, the above query string should have looked like: `"param[a]=1&param[b]=2&param[c]=3&param[d]=4&param[e]=5"` – Chris Hall Dec 16 '14 at 18:22
  • @Markus I don't understand where you are getting the second version from. I said nothing like that at all. – Ariel Dec 17 '14 at 08:55
  • @Ariel It's based on your previous comment about passing arrays into `param` having to be based on the output of `serializeArray`. You claimed I was being "overly literal" when I said it only applied when the only argument was an array. Based on my understanding of what you said, I thought you meant that it should always expect arrays to be in that format - regardless of where they appear in the input. – Chris Hall Dec 18 '14 at 20:42
  • Nice, this will work perfect with Json.Stringify(object)! – eaglei22 Mar 30 '17 at 22:35
  • 7
    Question has specifically asked Vanilla JS – Bug Hunter 219 Dec 26 '17 at 10:57
169

I suggest using the URLSearchParams interface:

const searchParams = new URLSearchParams();
const params = {foo: "hi there", bar: "100%" };
Object.keys(params).forEach(key => searchParams.append(key, params[key]));
console.log(searchParams.toString())

Or by passing the search object into the constructor like this:

const params = {foo: "hi there", bar: "100%" };
const queryString = new URLSearchParams(params).toString();
console.log(queryString);
glinda93
  • 7,659
  • 5
  • 40
  • 78
bmaggi
  • 2,908
  • 1
  • 19
  • 16
  • If you're not gonna support IE (which is pretty common now) and some specific mobile versions, this is the best answer, as it is plain JavaScript. – Marcos Sandrini Jan 23 '19 at 10:13
  • 5
    @bmaggi doesn't work with nested properties {a: { 1: 'test', 2: 'test2'}} Expected: `a[1]=test&a[2]=test2` – SergkeiM Feb 19 '21 at 08:40
  • @bravemaster It's a great solution, especially for node developers. Thank you! – Krzysztof Szala Mar 24 '21 at 15:47
  • Note that these days, in modern environments, you can use `Object.entries` if your starting point is an object as in the above: `const searchParams = new URLSearchParams(Object.entries(params));` – T.J. Crowder May 31 '21 at 09:20
  • Does this support object nesting? – Dimitri Kopriwa Aug 10 '21 at 15:04
  • Note that you have to validate fields before using this method. E.g. if some field has an empty string the query string will be like `.../?orderby=&page=1`. Of course, this can be handled on the backend but better to make things safe. – JM217 Oct 26 '21 at 03:14
139

Use:

Object.keys(obj).reduce(function(a,k){a.push(k+'='+encodeURIComponent(obj[k]));return a},[]).join('&')

I like this one-liner, but I bet it would be a more popular answer if it matched the accepted answer semantically:

function serialize( obj ) {
    let str = '?' + Object.keys(obj).reduce(function(a, k){
        a.push(k + '=' + encodeURIComponent(obj[k]));
        return a;
    }, []).join('&');
    return str;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
sergk
  • 3,591
  • 1
  • 20
  • 14
  • 1
    A dedicated line for the reduct function would greatly improve the readability though. – Aurelien Ribon Apr 20 '15 at 14:48
  • 60
    Using .map() instead of .reduce() would be even simpler: `Object.keys(obj).map(k => k + '=' + encodeURIComponent(obj[k])).join('&')` – Jannes Meyer Apr 21 '15 at 13:34
  • Great solution, thanks! Just a hint: the key should also be URI encoded. Although unlikely, it can contain characters that need encoding. – Simon Steinberger May 10 '15 at 22:22
  • 2
    Just to note that `Object.keys` is only available in IE >= 9 – Johnston Jun 02 '15 at 20:26
  • 6
    Further improved @Jannes code using ES6 templates instead of concatenation - ```Object.keys(obj).map(k => `${k}=${encodeURIComponent(obj[k])}`).join('&')``` – csilk Jan 25 '17 at 00:16
  • 1
    In case your keys need encodeUriComponent ❤️ too: `Object.entries(obj).map(e => e.map(ee => encodeURIComponent(ee)).join('=')).join('&');` – Blaise Nov 04 '17 at 19:45
  • 3
    This answer would be even more popular if you used a less generic term than `serialize` for your edit's function name, perhaps `encodeAsQueryString`. Otherwise, everyone has to rename it for actual use - or worse, not rename it. – Edward Brey Feb 26 '18 at 19:21
  • 1
    this answer serializes only shallow objects, does not work with `{ a: { b: 1 }}` – Fabrizio Bertoglio May 08 '19 at 14:55
  • 1
    @csilk you can use entries instance of keys `Object.entries(obj).map(([key, value]) => `${key}=${value}`).join('&')` – Kevin Mendez May 31 '21 at 23:58
120

Here's a one liner in ES6:

Object.keys(obj).map(k => `${encodeURIComponent(k)}=${encodeURIComponent(obj[k])}`).join('&');
Nico Tejera
  • 1,309
  • 1
  • 8
  • 3
  • replace key w/ k and you're golden – Jamie Kudla Feb 24 '16 at 17:02
  • 22
    Warning! This only works on shallow objects. If you have a top-level property that is another object, this one liner will output "key=%5Bobject%20Object%5D". Just as a heads up. – Andrew Allbright Apr 05 '16 at 18:47
  • 7
    Also, this doesn't spit up arrays. I got `export?actions[]=finance,create,edit` when it should have `export?actions[]=finance&actions[]=create&actions[]=edit` as is the awful standard. – darkbluesun Aug 11 '16 at 03:15
  • 6
    Arrays are pretty much always "you're on your own" because URL arguments are just strings as far as the spec is concerned, so you're on the hook to make anything that isn't a single string gets read correctly by the server you're calling. `actions[]` is PHP notation; Django uses multiple `action` instead (no `[]` suffix); some other ORM/CMS require comma-separated lists, etc. So "if it's not simple strings, first make sure you know what your server even wants". – Mike 'Pomax' Kamermans Jan 23 '17 at 19:01
  • do we really need `encodeURIComponent()` on the key as well? – igorsantos07 Jun 15 '18 at 01:12
  • Personally I like to filter out empty values, hence, I add before the `map`: `Object.keys(obj).filter(k => obj[k])`. – Augustin Riedinger Dec 10 '19 at 16:32
65

With Node.js v6.6.3

const querystring = require('querystring')

const obj = {
  foo: 'bar',
  baz: 'tor'
}

let result = querystring.stringify(obj)
// foo=bar&baz=tor

Reference: Query string

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
31

Ruby on Rails and PHP style query builder

This method converts a JavaScript object into a URI query string. It also handles nested arrays and objects (in Ruby on Rails and PHP syntax):

function serializeQuery(params, prefix) {
  const query = Object.keys(params).map((key) => {
    const value  = params[key];

    if (params.constructor === Array)
      key = `${prefix}[]`;
    else if (params.constructor === Object)
      key = (prefix ? `${prefix}[${key}]` : key);

    if (typeof value === 'object')
      return serializeQuery(value, key);
    else
      return `${key}=${encodeURIComponent(value)}`;
  });

  return [].concat.apply([], query).join('&');
}

Example Usage:

let params = {
  a: 100,
  b: 'has spaces',
  c: [1, 2, 3],
  d: { x: 9, y: 8}
}

serializeQuery(params)
// returns 'a=100&b=has%20spaces&c[]=1&c[]=2&c[]=3&d[x]=9&d[y]=8
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sheharyar
  • 73,588
  • 21
  • 168
  • 215
  • 1
    Nice example. I fixed a typo in your answer. By the way, would be interesting if you edit your `function` to exclude `falsy` values (null, undefined, NaN, '')... – developer033 Apr 29 '17 at 22:44
  • 1
    This is a good example to solve this problem with a well written and incorporates the recursion and type checking needed to solve this issue. – Noah Jul 14 '20 at 17:42
26

A small amendment to the accepted solution by user187291:

serialize = function(obj) {
   var str = [];
   for(var p in obj){
       if (obj.hasOwnProperty(p)) {
           str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
       }
   }
   return str.join("&");
}

Checking for hasOwnProperty on the object makes JSLint and JSHint happy, and it prevents accidentally serializing methods of the object or other stuff if the object is anything but a simple dictionary. See the paragraph on for statements on Code Conventions for the JavaScript Programming Language.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
jssebastian
  • 1,189
  • 1
  • 11
  • 12
20

Here is a one-liner:

const encoded = Object.entries(obj).map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join("&");
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Alex Escalante
  • 447
  • 4
  • 11
  • 2
    Object.entries is not supported in IE. – MBouwman Apr 18 '19 at 13:28
  • @MBouwman of course, IE is broken beyond good and evil, that's why you have to use babel/core-js – chpio Jul 03 '19 at 16:27
  • @chpio Babel/core-js does not support Object.entries if I'm right. – MBouwman Sep 20 '19 at 12:18
  • core has support for Object.entries: https://github.com/zloirock/core-js/blob/master/README.md#ecmascript-object and even the old corejs2 babel runtime transform does support it as well https://github.com/babel/babel/blob/962015f7e73a4ffcec4f77fa670b47682edf53b0/packages/babel-plugin-transform-runtime/src/runtime-corejs2-definitions.js#L38 – chpio Sep 23 '19 at 14:00
12

If you need to send arbitrary objects, then GET is a bad idea since there are limits to the lengths of URLs that user agents and web servers will accepts. My suggestion would be to build up an array of name-value pairs to send and then build up a query string:

function QueryStringBuilder() {
    var nameValues = [];

    this.add = function(name, value) {
        nameValues.push( {name: name, value: value} );
    };

    this.toQueryString = function() {
        var segments = [], nameValue;
        for (var i = 0, len = nameValues.length; i < len; i++) {
            nameValue = nameValues[i];
            segments[i] = encodeURIComponent(nameValue.name) + "=" + encodeURIComponent(nameValue.value);
        }
        return segments.join("&");
    };
}

var qsb = new QueryStringBuilder();
qsb.add("veg", "cabbage");
qsb.add("vegCount", "5");

alert( qsb.toQueryString() );
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Tim Down
  • 318,141
  • 75
  • 454
  • 536
7

Here's the CoffeeScript version of the accepted answer.

serialize = (obj, prefix) ->
  str = []
  for p, v of obj
    k = if prefix then prefix + "[" + p + "]" else p
    if typeof v == "object"
      str.push(serialize(v, k))
    else
      str.push(encodeURIComponent(k) + "=" + encodeURIComponent(v))

  str.join("&")
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
alf
  • 18,372
  • 10
  • 61
  • 92
7

A little bit look better

objectToQueryString(obj, prefix) {
    return Object.keys(obj).map(objKey => {
        if (obj.hasOwnProperty(objKey)) {
            const key = prefix ? `${prefix}[${objKey}]` : objKey;
            const value = obj[objKey];

            return typeof value === "object" ?
                this.objectToQueryString(value, key) :
                `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
        }

        return null;
    }).join("&");
}
Community
  • 1
  • 1
Pavel Perevezencev
  • 2,596
  • 3
  • 28
  • 49
7

This one skips null/undefined values

export function urlEncodeQueryParams(data) {
    const params = Object.keys(data).map(key => data[key] ? `${encodeURIComponent(key)}=${encodeURIComponent(data[key])}` : '');
    return params.filter(value => !!value).join('&');
}
nimatra
  • 604
  • 8
  • 19
7

Use:

const toQueryString = obj => "?".concat(Object.keys(obj).map(e => `${encodeURIComponent(e)}=${encodeURIComponent(obj[e])}`).join("&"));

const data = {
  offset: 5,
  limit: 10
};

toQueryString(data); // => ?offset=5&limit=10

Or use a predefined feature

const data = {
  offset: 5,
  limit: 10
};

new URLSearchParams(data).toString(); // => ?offset=5&limit=10

Note

Both the above methods will set the value as null if not present. If you want not to set the query parameter if value is null then use:

const toQueryString = obj => "?".concat(Object.keys(obj).map(e => obj[e] ? `${encodeURIComponent(e)}=${encodeURIComponent(obj[e])}` : null).filter(e => !!e).join("&"));


const data = {
  offset: null,
  limit: 10
};

toQueryString(data); // => "?limit=10" else with above methods "?offset=null&limit=10"

You can freely use any method.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Bhuvnesh
  • 211
  • 4
  • 8
  • 1
    URLSearchParams should be the only answer at this point in history. Setting value == null is done because it will always evaluate as falsey consistently. This allows the service endpoint to consistently check the parameter value as truthy or falsey without the extra checks that come with String of length == 0 or String == undefined. Your solution to exclude if null works, but I think it's a better design pattern to leave null values there since most APIs are going to be checking for them anyway in most scenarios. – Wes Apr 01 '22 at 17:39
6

Here's a concise & recursive version with Object.entries. It handles arbitrarily nested arrays, but not nested objects. It also removes empty elements:

const format = (k,v) => v !== null ? `${k}=${encodeURIComponent(v)}` : ''

const to_qs = (obj) => {
    return [].concat(...Object.entries(obj)
                       .map(([k,v]) => Array.isArray(v) 
                          ? v.map(arr => to_qs({[k]:arr})) 
                          : format(k,v)))
           .filter(x => x)
           .join('&');
}

E.g.:

let json = { 
    a: [1, 2, 3],
    b: [],              // omit b
    c: 1,
    d: "test&encoding", // uriencode
    e: [[4,5],[6,7]],   // flatten this
    f: null,            // omit nulls
    g: 0
};

let qs = to_qs(json)

=> "a=1&a=2&a=3&c=1&d=test%26encoding&e=4&e=5&e=6&e=7&g=0"
mikebridge
  • 4,209
  • 2
  • 40
  • 50
  • This version did the job for me when dealing with nested arrays. Made a slight tweak to use Ruby/PHP-style array keys but otherwise works great. – nickb Nov 27 '18 at 22:15
5

I have a simpler solution that does not use any third-party library and is already apt to be used in any browser that has "Object.keys" (aka all modern browsers + Edge + Internet Explorer):

In ES5:

function(a){
    if( typeof(a) !== 'object' )
        return '';
    return `?${Object.keys(a).map(k=>`${k}=${a[k]}`).join('&')}`;
}

In ES3:

function(a){
    if( typeof(a) !== 'object' )
        return '';
    return '?' + Object.keys(a).map(function(k){ return k + '=' + a[k] }).join('&');
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
LeandroLuk
  • 154
  • 1
  • 4
  • 11
5

I made a comparison of JSON stringifiers and the results are as follows:

JSON:    {"_id":"5973782bdb9a930533b05cb2","isActive":true,"balance":"$1,446.35","age":32,"name":"Logan Keller","email":"logankeller@artiq.com","phone":"+1 (952) 533-2258","friends":[{"id":0,"name":"Colon Salazar"},{"id":1,"name":"French Mcneil"},{"id":2,"name":"Carol Martin"}],"favoriteFruit":"banana"}
Rison:   (_id:'5973782bdb9a930533b05cb2',age:32,balance:'$1,446.35',email:'logankeller@artiq.com',favoriteFruit:banana,friends:!((id:0,name:'Colon Salazar'),(id:1,name:'French Mcneil'),(id:2,name:'Carol Martin')),isActive:!t,name:'Logan Keller',phone:'+1 (952) 533-2258')
O-Rison: _id:'5973782bdb9a930533b05cb2',age:32,balance:'$1,446.35',email:'logankeller@artiq.com',favoriteFruit:banana,friends:!((id:0,name:'Colon Salazar'),(id:1,name:'French Mcneil'),(id:2,name:'Carol Martin')),isActive:!t,name:'Logan Keller',phone:'+1 (952) 533-2258'
JSURL:   ~(_id~'5973782bdb9a930533b05cb2~isActive~true~balance~'!1*2c446.35~age~32~name~'Logan*20Keller~email~'logankeller*40artiq.com~phone~'*2b1*20*28952*29*20533-2258~friends~(~(id~0~name~'Colon*20Salazar)~(id~1~name~'French*20Mcneil)~(id~2~name~'Carol*20Martin))~favoriteFruit~'banana)
QS:      _id=5973782bdb9a930533b05cb2&isActive=true&balance=$1,446.35&age=32&name=Logan Keller&email=logankeller@artiq.com&phone=+1 (952) 533-2258&friends[0][id]=0&friends[0][name]=Colon Salazar&friends[1][id]=1&friends[1][name]=French Mcneil&friends[2][id]=2&friends[2][name]=Carol Martin&favoriteFruit=banana
URLON:   $_id=5973782bdb9a930533b05cb2&isActive:true&balance=$1,446.35&age:32&name=Logan%20Keller&email=logankeller@artiq.com&phone=+1%20(952)%20533-2258&friends@$id:0&name=Colon%20Salazar;&$id:1&name=French%20Mcneil;&$id:2&name=Carol%20Martin;;&favoriteFruit=banana
QS-JSON: isActive=true&balance=%241%2C446.35&age=32&name=Logan+Keller&email=logankeller%40artiq.com&phone=%2B1+(952)+533-2258&friends(0).id=0&friends(0).name=Colon+Salazar&friends(1).id=1&friends(1).name=French+Mcneil&friends(2).id=2&friends(2).name=Carol+Martin&favoriteFruit=banana

The shortest among them is URL Object Notation.

niutech
  • 28,923
  • 15
  • 96
  • 106
5

There another popular library, qs. You can add it by:

yarn add qs

And then use it like this:

import qs from 'qs'

const array = { a: { b: 'c' } }
const stringified = qs.stringify(array, { encode: false })

console.log(stringified) //-- outputs a[b]=c
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
dMedia
  • 809
  • 1
  • 10
  • 18
4

This is an addition for the accepted solution. This works with objects and array of objects:

parseJsonAsQueryString = function (obj, prefix, objName) {
    var str = [];
    for (var p in obj) {
        if (obj.hasOwnProperty(p)) {
            var v = obj[p];
            if (typeof v == "object") {
                var k = (objName ? objName + '.' : '') + (prefix ? prefix + "[" + p + "]" : p);
                str.push(parseJsonAsQueryString(v, k));
            } else {
                var k = (objName ? objName + '.' : '') + (prefix ? prefix + '.' + p : p);
                str.push(encodeURIComponent(k) + "=" + encodeURIComponent(v));
                //str.push(k + "=" + v);
            }
        }
    }
    return str.join("&");
}

Also I have added objName if you're using object parameters, like in ASP.NET MVC action methods.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
4

A single line to convert an object into a query string in case somebody needs it again:

let Objs = { a: 'obejct-a', b: 'object-b' }

Object.keys(objs).map(key => key + '=' + objs[key]).join('&')

// The result will be a=object-a&b=object-b
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
4

In ES7, you can write this in one line:

const serialize = (obj) => (Object.entries(obj).map(i => [i[0], encodeURIComponent(i[1])].join('=')).join('&'))
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Pavlo Sadovyi
  • 79
  • 1
  • 5
4

ES6 solution for query string encoding of a JavaScript object

const params = {
  a: 1,
  b: 'query stringify',
  c: null,
  d: undefined,
  f: '',
  g: { foo: 1, bar: 2 },
  h: ['Winterfell', 'Westeros', 'Braavos'],
  i: { first: { second: { third: 3 }}}
}

static toQueryString(params = {}, prefix) {
  const query = Object.keys(params).map((k) => {
    let key = k;
    const value = params[key];

    if (!value && (value === null || value === undefined || isNaN(value))) {
      value = '';
    }

    switch (params.constructor) {
      case Array:
        key = `${prefix}[]`;
        break;
      case Object:
        key = (prefix ? `${prefix}[${key}]` : key);
        break;
    }

    if (typeof value === 'object') {
      return this.toQueryString(value, key); // for nested objects
    }

    return `${key}=${encodeURIComponent(value)}`;
  });

  return query.join('&');
}

toQueryString(params)

"a=1&b=query%20stringify&c=&d=&f=&g[foo]=1&g[bar]=2&h[]=Winterfell&h[]=Westeros&h[]=Braavos&i[first][second][third]=3"
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
gandharv garg
  • 1,781
  • 12
  • 17
3

If you want to convert a nested object recursively and the object may or may not contain arrays (and the arrays may contain objects or arrays, etc), then the solution gets a little more complex. This is my attempt.

I've also added some options to choose if you want to record for each object member at what depth in the main object it sits, and to choose if you want to add a label to the members that come from converted arrays.

Ideally you should test if the thing parameter really receives an object or array.

function thingToString(thing,maxDepth,recordLevel,markArrays){
    //thing: object or array to be recursively serialized
    //maxDepth (int or false):
    // (int) how deep to go with converting objects/arrays within objs/arrays
    // (false) no limit to recursive objects/arrays within objects/arrays
    //recordLevel (boolean):
    //  true - insert "(level 1)" before transcript of members at level one (etc)
    //  false - just 
    //markArrays (boolean):
    //  insert text to indicate any members that came from arrays
    var result = "";
    if (maxDepth !== false && typeof maxDepth != 'number') {maxDepth = 3;}
    var runningDepth = 0;//Keeps track how deep we're into recursion

    //First prepare the function, so that it can call itself recursively
    function serializeAnything(thing){
        //Set path-finder values
        runningDepth += 1;
        if(recordLevel){result += "(level " + runningDepth + ")";}

        //First convert any arrays to object so they can be processed
        if (thing instanceof Array){
            var realObj = {};var key;
            if (markArrays) {realObj['type'] = "converted array";}
            for (var i = 0;i < thing.length;i++){
                if (markArrays) {key = "a" + i;} else {key = i;}
                realObj[key] = thing[i];
            }
            thing = realObj;
            console.log('converted one array to ' + typeof realObj);
            console.log(thing);
        }

        //Then deal with it
        for (var member in thing){
            if (typeof thing[member] == 'object' && runningDepth < maxDepth){
                serializeAnything(thing[member]);
                //When a sub-object/array is serialized, it will add one to
                //running depth. But when we continue to this object/array's
                //next sibling, the level must go back up by one
                runningDepth -= 1;
            } else if (maxDepth !== false && runningDepth >= maxDepth) {
                console.log('Reached bottom');
            } else 
            if (
                typeof thing[member] == "string" || 
                typeof thing[member] == 'boolean' ||
                typeof thing[member] == 'number'
            ){
                result += "(" + member + ": " + thing[member] + ") ";
            }  else {
                result += "(" + member + ": [" + typeof thing[member] + " not supported]) ";
            }
        }
    }
    //Actually kick off the serialization
    serializeAnything(thing);

    return result;

}
Wytze
  • 7,844
  • 8
  • 49
  • 62
3

This is a solution that will work for .NET backends out of the box. I have taken the primary answer of this thread and updated it to fit our .NET needs.

function objectToQuerystring(params) {
var result = '';

    function convertJsonToQueryString(data, progress, name) {
        name = name || '';
        progress = progress || '';
        if (typeof data === 'object') {
            Object.keys(data).forEach(function (key) {
                var value = data[key];
                if (name == '') {
                    convertJsonToQueryString(value, progress, key);
                } else {
                    if (isNaN(parseInt(key))) {
                        convertJsonToQueryString(value, progress, name + '.' + key);
                    } else {
                        convertJsonToQueryString(value, progress, name + '[' + key+ ']');
                    }
                }
            })
        } else {
            result = result ? result.concat('&') : result.concat('?');
            result = result.concat(`${name}=${data}`);
        }
    }

    convertJsonToQueryString(params);
    return result;
}
Angel Venchev
  • 697
  • 1
  • 7
  • 18
3

To do it in a better way.

It can handle recursive objects or arrays in the standard query form, like a=val&b[0]=val&b[1]=val&c=val&d[some key]=val. Here's the final function.

Logic, Functionality

const objectToQueryString = (initialObj) => {
  const reducer = (obj, parentPrefix = null) => (prev, key) => {
    const val = obj[key];
    key = encodeURIComponent(key);
    const prefix = parentPrefix ? `${parentPrefix}[${key}]` : key;

    if (val == null || typeof val === 'function') {
      prev.push(`${prefix}=`);
      return prev;
    }

    if (['number', 'boolean', 'string'].includes(typeof val)) {
      prev.push(`${prefix}=${encodeURIComponent(val)}`);
      return prev;
    }

    prev.push(Object.keys(val).reduce(reducer(val, prefix), []).join('&'));
    return prev;
  };

  return Object.keys(initialObj).reduce(reducer(initialObj), []).join('&');
};

Example

const testCase1 = {
  name: 'Full Name',
  age: 30
}

const testCase2 = {
  name: 'Full Name',
  age: 30,
  children: [
    {name: 'Child foo'},
    {name: 'Foo again'}
  ],
  wife: {
    name: 'Very Difficult to say here'
  }
}

console.log(objectToQueryString(testCase1));
console.log(objectToQueryString(testCase2));

Live Test

Expand the snippet below to verify the result in your browser -

const objectToQueryString = (initialObj) => {
  const reducer = (obj, parentPrefix = null) => (prev, key) => {
    const val = obj[key];
    key = encodeURIComponent(key);
    const prefix = parentPrefix ? `${parentPrefix}[${key}]` : key;

    if (val == null || typeof val === 'function') {
      prev.push(`${prefix}=`);
      return prev;
    }

    if (['number', 'boolean', 'string'].includes(typeof val)) {
      prev.push(`${prefix}=${encodeURIComponent(val)}`);
      return prev;
    }

    prev.push(Object.keys(val).reduce(reducer(val, prefix), []).join('&'));
    return prev;
  };

  return Object.keys(initialObj).reduce(reducer(initialObj), []).join('&');
};

const testCase1 = {
  name: 'Full Name',
  age: 30
}

const testCase2 = {
  name: 'Full Name',
  age: 30,
  children: [
    {name: 'Child foo'},
    {name: 'Foo again'}
  ],
  wife: {
    name: 'Very Difficult to say here'
  }
}

console.log(objectToQueryString(testCase1));
console.log(objectToQueryString(testCase2));

Things to consider.

  • It skips values for functions, null, and undefined
  • It skips keys and values for empty objects and arrays.
  • It doesn't handle Number or String objects made with new Number(1) or new String('my string') because no one should ever do that
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
dipenparmar12
  • 3,042
  • 1
  • 29
  • 39
  • 1
    Maybe change "if (val == null" to "if (val === null" (the ===). But this worked pretty well. – Asfo Nov 10 '21 at 20:03
2

The previous answers do not work if you have a lot of nested objects.

Instead you can pick the function parameter from jquery-param/jquery-param.js. It worked very well for me!

    var param = function (a) {
    var s = [], rbracket = /\[\]$/,
        isArray = function (obj) {
            return Object.prototype.toString.call(obj) === '[object Array]';
        }, add = function (k, v) {
            v = typeof v === 'function' ? v() : v === null ? '' : v === undefined ? '' : v;
            s[s.length] = encodeURIComponent(k) + '=' + encodeURIComponent(v);
        }, buildParams = function (prefix, obj) {
            var i, len, key;

            if (prefix) {
                if (isArray(obj)) {
                    for (i = 0, len = obj.length; i < len; i++) {
                        if (rbracket.test(prefix)) {
                            add(prefix, obj[i]);
                        } else {
                            buildParams(prefix + '[' + (typeof obj[i] === 'object' ? i : '') + ']', obj[i]);
                        }
                    }
                } else if (obj && String(obj) === '[object Object]') {
                    for (key in obj) {
                        buildParams(prefix + '[' + key + ']', obj[key]);
                    }
                } else {
                    add(prefix, obj);
                }
            } else if (isArray(obj)) {
                for (i = 0, len = obj.length; i < len; i++) {
                    add(obj[i].name, obj[i].value);
                }
            } else {
                for (key in obj) {
                    buildParams(key, obj[key]);
                }
            }
            return s;
        };

    return buildParams('', a).join('&').replace(/%20/g, '+');
};
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
1

ok, it's a older post but i'm facing this problem and i have found my personal solution.. maybe can help someone else..

     function objToQueryString(obj){
        var k = Object.keys(obj);
        var s = "";
        for(var i=0;i<k.length;i++) {
            s += k[i] + "=" + encodeURIComponent(obj[k[i]]);
            if (i != k.length -1) s += "&";
        }
        return s;
     };
hayatoShingu
  • 418
  • 5
  • 11
1

I've written a package just for that: object-query-string :)

It supports nested objects, arrays, custom encoding functions, etc. It is lightweight and jQuery-free.

// TypeScript
import { queryString } from 'object-query-string';

// Node.js
const { queryString } = require("object-query-string");

const query = queryString({
    filter: {
        brands: ["Audi"],
        models: ["A4", "A6", "A8"],
        accidentFree: true
    },
    sort: 'mileage'
});

returns

filter[brands][]=Audi&filter[models][]=A4&filter[models][]=A6&filter[models][]=A8&filter[accidentFree]=true&sort=milage
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ΔO 'delta zero'
  • 3,506
  • 1
  • 19
  • 31
1

After going through some top answers here, I have wrote another implementation that tackles some edge cases as well

function serialize(params, prefix) {                
    return Object.entries(params).reduce((acc, [key, value]) => {
        // remove whitespace from both sides of the key before encoding
        key = encodeURIComponent(key.trim());

        if (params.constructor === Array ) {
          key = `${prefix}[]`;
        } else if (params.constructor === Object) {
          key = (prefix ? `${prefix}[${key}]` : key);
        }

        /**
         *  - undefined and NaN values will be skipped automatically
         *  - value will be empty string for functions and null
         *  - nested arrays will be flattened
         */
        if (value === null || typeof value === 'function') {
            acc.push(`${key}=`);
        } else if (typeof value === 'object') {
            acc = acc.concat(serialize(value, key));
        } else if(['number', 'boolean', 'string'].includes(typeof value) && value === value) { // self-check to avoid NaN
            acc.push(`${key}=${encodeURIComponent(value)}`);
        }

        return acc;
    }, []);
}

function objectToQueryString(queryParameters) {
    return queryParameters ? serialize(queryParameters).join('&'): '';
}

let x = objectToQueryString({
    foo: 'hello world',
    bar: {
      blah: 123,
      list: [1, 2, 3],
        'nested array': [[4,5],[6,7]] // will be flattened
    },
    page: 1,
    limit: undefined, // field will be ignored
    check: false,
    max: NaN, // field will be ignored
    prop: null,
    ' key value': 'with spaces' // space in key will be trimmed out
});
  
console.log(x); // foo=hello%20world&bar[blah]=123&bar[list][]=1&bar[list][]=2&bar[list][]=3&bar[nested%20array][][]=4&bar[nested%20array][][]=5&bar[nested%20array][][]=6&bar[nested%20array][][]=7&page=1&check=false&prop=&key%20value=with%20spaces
abhijithvijayan
  • 835
  • 12
  • 17
1

TypeScript version for PHP notation (no URL escaped version)

/**
 * Converts an object into a Cookie-like string.
 * @param toSerialize object or array to be serialized
 * @param prefix used in deep objects to describe the final query parameter
 * @returns ampersand separated key=value pairs
 *
 * Example:
 * ```js
 * serialize({hello:[{world: "nice"}]}); // outputs  "hello[0][world]=nice"
 * ```
 * ---
 * Adapted to TS from a StackOverflow answer https://stackoverflow.com/a/1714899/4537906
 */
const serialize = (toSerialize: unknown = {}, prefix?: string) => {
  const keyValuePairs = [];

  Object.keys(toSerialize).forEach((attribute) => {
    if (Object.prototype.hasOwnProperty.call(toSerialize, attribute)) {
      const key = prefix ? `${prefix}[${attribute}]` : attribute;
      const value = toSerialize[attribute];
      const toBePushed =
        value !== null && typeof value === "object"
          ? serialize(value, key)
          : `${key}=${value}`;
      keyValuePairs.push(toBePushed);
    }
  });

  return keyValuePairs.join("&");
};
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Soldeplata Saketos
  • 3,212
  • 1
  • 25
  • 38
1

URLSearchParams looks good, but it didn't work for nested objects.

Try to use

encodeURIComponent(JSON.stringify(object))
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
0

Just another way (no recursive object):

   getQueryString = function(obj)
   {
      result = "";

      for(param in obj)
         result += ( encodeURIComponent(param) + '=' + encodeURIComponent(obj[param]) + '&' );

      if(result) //it's not empty string when at least one key/value pair was added. In such case we need to remove the last '&' char
         result = result.substr(0, result.length - 1); //If length is zero or negative, substr returns an empty string [ref. http://msdn.microsoft.com/en-us/library/0esxc5wy(v=VS.85).aspx]

      return result;
   }

alert( getQueryString({foo: "hi there", bar: 123, quux: 2 }) );
Marco Demaio
  • 33,578
  • 33
  • 128
  • 159
0

Referring to the answer user187291, add "isArray" as a parameter to make the JSON nested array be converted.

data : {
    staffId : "00000001",
    Detail : [ {
        "identityId" : "123456"
    }, {
        "identityId" : "654321"
    } ],

}

To make the result,

staffId=00000001&Detail[0].identityId=123456&Detail[1].identityId=654321

use:

serialize = function(obj, prefix, isArray) {
    var str = [], p = 0;
    for (p in obj) {
        if (obj.hasOwnProperty(p)) {
            var k, v;
            if (isArray)
                k = prefix ? prefix + "[" + p + "]" : p, v = obj[p];
            else
                k = prefix ? prefix + "." + p + "" : p, v = obj[p];

            if (v !== null && typeof v === "object") {
                if (Array.isArray(v)) {
                    serialize(v, k, true);
                } else {
                    serialize(v, k, false);
                }
            } else {
                var query = k + "=" + v;
                str.push(query);
            }
        }
    }
    return str.join("&");
};

serialize(data, "prefix", false);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Long Ranger
  • 5,888
  • 8
  • 43
  • 72
0

Here is a simple implementation that gets an object and converts it to a query parameters string:

export function objectToQueryParams(queryParams: object): string {
  return queryParams ?
    Object.entries(queryParams).reduce((acc, [key, val], index) => {
      const sign = index === 0 ? '?' : '&';
      acc += `${sign}${encodeURIComponent(key)}=${encodeURIComponent(val)}`;
      return acc;
    }, '')
    : '';
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
itay oded
  • 978
  • 13
  • 22
  • 1
    You need `encodeURIComponent` around `key` and `val` like every other response here – sbk Jan 24 '20 at 13:03
0

My implementation of encoding an object as a query string, using reduce:

export const encodeAsQueryString = (params) => (
  Object.keys(params).reduce((acc, key)=>(
    params.hasOwnProperty(key) ? (
      [...acc, encodeURIComponent(key) + '=' + encodeURIComponent(params[key])]
    ) : acc
  ), []).join('&')
);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
AntonAL
  • 16,692
  • 21
  • 80
  • 114
0

If you want to pass an entire object as a single parameter, e.g, ?filter={param1: "val1", param2: "val2"}:

const serializeObject = (obj) => {
  let objStr = JSON.stringify(obj);

  objStr = objStr.replace(/\{/g, encodeURIComponent("{"));
  objStr = objStr.replace(/}/g, encodeURIComponent("}"));
  objStr = objStr.replace(/:/g, encodeURIComponent(":"));

  return objStr;
};

let res = serializeObject({param1: "val1", param2: "val2"});
console.log("serializeObject:", res); //%7B"param1"%3A"val1","param2"%3A"val2"%7D
console.log("serializeObject-decoded:", decodeURIComponent(res)); //{"param1":"val1","param2":"val2"}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sergey Onishchenko
  • 6,943
  • 4
  • 44
  • 51
0

Use:

const objectToQueryParams = (o = {}) =>
  Object.entries(o)
    .map((p) => `${encodeURIComponent(p[0])}=${encodeURIComponent(p[1])}`)
    .join("&");

Refer to the below gist for more: https://gist.github.com/bhaireshm

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Bhairesh M
  • 72
  • 10
-1

const buildSortedQuery = (args) => {
    return Object.keys(args)
        .sort()
        .map(key => {
            return window.encodeURIComponent(key)
                + '='
                + window.encodeURIComponent(args[key]);
        })
        .join('&');
};

console.log(buildSortedQuery({
  foo: "hi there",
  bar: "100%"
}));

//bar=100%25&foo=hi%20there
Anthony Zhan
  • 861
  • 8
  • 11
-1

Just use the following:

encodeURIComponent(JSON.stringify(obj))

// elastic search example
let story ={
  "query": {
    "bool": {
      "must": [
        {
          "term": { 
            "revision.published": 0, 
          }
        },
        {
          "term": { 
            "credits.properties.by.properties.name": "Michael Guild"
          }
        },
        {
          "nested": {
            "path": "taxonomy.sections",
            "query": {
              "bool": {
                "must": [
                  {
                    "term": {
                      "taxonomy.sections._id": "/science"
                    }
                  },
                  {
                    "term": {
                      "taxonomy.sections._website": "staging"
                    }
                  }
                ]
              }
            }
          }
        }
      ]
    }
  }
}


const whateva = encodeURIComponent(JSON.stringify(story))
console.log(whateva)
Mohammad
  • 21,175
  • 15
  • 55
  • 84
Michael Guild
  • 808
  • 9
  • 8
-1
const querystring=  {};

querystring.stringify = function (obj, sep = '&', eq = '=') {
  const escape = encodeURIComponent;
  const qs = [];
  let key = null;

  for (key in obj) if (obj.hasOwnProperty(key)) {
    qs.push(escape(key) + eq + escape(String(obj[key])));
  }
  return qs.join(sep);
};

Example:

const a  = querystring.stringify({a: 'all of them', b: true});
console.log(a);  // Output: a=all%20of%20them&b=true
silver daymon
  • 149
  • 1
  • 5
-1

While there are limits to query-string lengths that should be considered (for sending JSON data in HTTP/s GET calls versus using POST)...

JSON.stringify(yourJSON) will create a String from your JSON object.

Then just hex-encode it (link below).

That will work always versus various possible problems with base64 type URL encoding, UTF-8 characters, nested JSON objects and such.

References

JSON.stringify()

Encode String to HEX

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Darrell Teague
  • 4,132
  • 1
  • 26
  • 38
-1

You can pass an object to this function with undefined properties. If the property exist, it will be converted to a query string and the query string will be returned.

    function convertToQueryString(props) {
      const objQueryString = { ...props };

      for (const key in objQueryString) {
        if (!key) {
          delete objQueryString[key];
        }
      }

      const params = JSON.stringify(objQueryString);



      const qs = params
        .replace(/[/''""{}]/g, '')
        .replace(/[:]/g, '=')
        .replace(/[,]/g, '&');

      console.log(qs)

      return qs;
    }

    convertToQueryString({order: undefined, limit: 5, page: 1})
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Nov 25 '21 at 11:53
  • The output seems to be "`limit=5&page=1`". Is that correct? – Peter Mortensen Nov 27 '22 at 22:40
-1
 let data = {
    id:1,
    name:'Newuser'
    };
const getqueryParam = data => {
  let datasize = Object.keys(data).length;
  let initial = '?';

  Object.keys(data).map(function (key, index) {
    initial = initial.concat(`${key}=${data[key]}`);
    index != datasize - 1 && (initial = initial.concat('&'));
  });
  console.log(initial, 'MyqueryString');
  return initial;
};

console.log(getqueryParam(data))//You can get the query string here

If you have baseUrl means to get full query use 

baseUrl.concat(getqueryParam(data))

  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Dec 24 '21 at 08:32
-1

For TS

const convertQueryToString = (data: { [x: string]: any }): string => {
  const serialize = (obj: { [x: string]: any }, prefix?: string): string => {
    const str = [];
    let p;

    for (p in obj) {
      if (obj.hasOwnProperty(p)) {
        const k = prefix ? `${prefix}[${p}]` : p;
        const v = obj[p];

        str.push(
          v !== null && typeof v === 'object' ? serialize(v, k) : `${encodeURIComponent(k)}=${encodeURIComponent(v)}`
        );
      }
    }

    return str.join('&');
  };

  return serialize(data);
};
  • 1
    Welcome to SO! Please don't post code-only answers but add a little textual explanation about how and why your approach works and what makes it different from the other answers given. You can find out more at our ["How to write a good answer"](https://stackoverflow.com/help/how-to-answer) page. – ahuemmer Dec 26 '22 at 08:02
-2

You can also achieve this by using simple JavaScript.

const stringData = '?name=Nikhil&surname=Mahirrao&age=30';
    
const newData= {};
stringData.replace('?', '').split('&').map((value) => {
  const temp = value.split('=');
  newData[temp[0]] = temp[1];
});

console.log('stringData: '+stringData);
console.log('newData: ');
console.log(newData);
Nikhil Mahirrao
  • 3,547
  • 1
  • 25
  • 20
-2
const serialize = obj => Object.keys(obj).reduce((a, b) =>
    a.push(encodeURIComponent(b) + "=" + encodeURIComponent(obj[b])) && a,
    []).join("&");

Call:

console.log(serialize({a:1,b:2}));
// output: 'a=1&b=2

'

Behnam
  • 6,244
  • 1
  • 39
  • 36
  • **From review queue**: May I request you to please add some context around your source-code. Code-only answers are difficult to understand. It will help the asker and future readers both if you can add more information in your post. – RBT May 30 '19 at 09:54
-3

With Ramda:

    R.pipe(R.toPairs, R.map(R.join('=')), R.join('&'))({a: 'b', b: 'a'})
v.bortsov
  • 1
  • 1
  • 1
    This answer is specific to a framework, despite the OP *explicitly* stating they were looking for a vanilla Javascript solution: [“No jQuery, no other frameworks - just plain Javascript :)”](https://stackoverflow.com/q/1714786/1341949#mainbar) – Rafe Goldberg Mar 22 '22 at 14:58