New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to pass url query params? #256

Closed
vishalvijay opened this Issue Jan 9, 2016 · 30 comments

Comments

Projects
None yet
@vishalvijay

vishalvijay commented Jan 9, 2016

How to pass url query params?

For eg: http://www.abx.com?x=2&y=3

@mislav

This comment has been minimized.

Member

mislav commented Jan 9, 2016

The fetch() operation simply uses the exact URL string as it was given. To pass the URL query params, simply have them in the URL string as in our example above:

fetch("http://www.abx.com?x=2&y=3")

If you need to construct query param string from a hash and you use jQuery on your site, you can do:

var url = "http://www.abx.com?" + $.param({foo: "bar", baz: "kuuq"})
fetch(url)

Update: here is what the fetch standard recommends in their documentation (also see discussion in whatwg/fetch#56):

var url = new URL("https://geo.example.org/api"),
    params = {lat:35.696233, long:139.570431}
Object.keys(params).forEach(key => url.searchParams.append(key, params[key]))
fetch(url).then(...)

This requires that the browser implements url.searchParams. A polyfill might be needed for older browsers.

@mislav mislav closed this Jan 9, 2016

@dgraham

This comment has been minimized.

Member

dgraham commented Jan 9, 2016

Past discussions in #153 and #167.

@DannyiCracked

This comment has been minimized.

DannyiCracked commented Jul 9, 2016

If fetch is hoping to become a front runner as new technology to use it really should provide something as simple as accepting an object as a query string.

fetch('url.com', { qs: { a: 1, b: 2 } }) is pretty basic functionality offered by every other URI request technologies.

@ttaranov

This comment has been minimized.

ttaranov commented Jul 14, 2016

lack of structured get parameters is a rather curious decision on part of fetch - and not supporting it is a backwards step. As post above mentioned, every http request generation library has it, for valid reasons.

closing this ticket and other related without a solution is counterproductive.

@oeddyo

This comment has been minimized.

oeddyo commented Aug 22, 2016

Also curious can we have this feature?

@hbrls

This comment has been minimized.

hbrls commented Aug 23, 2016

I'm using this http://stackoverflow.com/a/34209399/707580

var params = {
    parameter1: 'value_1',
    parameter2: 'value 2',
    parameter3: 'value&3' 
};

var esc = encodeURIComponent;
var query = Object.keys(params)
    .map(k => esc(k) + '=' + esc(params[k]))
    .join('&');
@oeddyo

This comment has been minimized.

oeddyo commented Aug 23, 2016

@hbrls Thanks that worked for me. But the point here is to add the functionality so that fellow users can use fetch more easily

@mislav

This comment has been minimized.

Member

mislav commented Sep 8, 2016

@oeddyo We would have added this feature if it was in the fetch spec, but it isn't. We won't implement non-standard features in this library, since that would differ compared to browser implementations in Chrome, Firefox, and lately Safari and IE.

@mnpenner

This comment has been minimized.

mnpenner commented Nov 8, 2016

You can add in this feature yourself with something like this:

export function fetch2(url, options={}) {
    options = {
        // your default options
        credentials: 'same-origin',
        redirect: 'error',
        ...options,
    };

    if(options.queryParams) {
        url += (url.indexOf('?') === -1 ? '?' : '&') + queryParams(options.queryParams);
        delete options.queryParams;
    }

    return fetch(url, options);
}

function queryParams(params) {
    return Object.keys(params)
        .map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k]))
        .join('&');
}
@sebringj

This comment has been minimized.

sebringj commented Dec 13, 2016

Here's a little API whip up from these comments as an ES6 export

    function getQueryString(params) {
      var esc = encodeURIComponent;
      return Object.keys(params)
        .map(k => esc(k) + '=' + esc(params[k]))
        .join('&');
    }

    function request(params) {
      var method = params.method || 'GET';
      var qs = '';
      var body;
      var headers = params.headers || {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
      };

      if (['GET', 'DELETE'].indexOf(method) > -1)
        qs = '?' + getQueryString(params.data);
      else // POST or PUT
        body = JSON.stringify(params.data);

      var url = params.url + qs;

      return fetch(url, { method, headers, body });
    }

    export default {
      get: params => request(Object.assign({ method: 'GET' }, params)),
      post: params => request(Object.assign({ method: 'POST' }, params)),
      put: params => request(Object.assign({ method: 'PUT' }, params)),
      delete: params => request(Object.assign({ method: 'DELETE' }, params))
    };

So you'd use it like so:

var soFetch = require('./soFetch');

soFetch.get({ url: 'https://sumpin', data: {  })
  .then(response => console.log(response.json()));

@notgiorgi

This comment has been minimized.

notgiorgi commented Jan 11, 2017

How about if we have an array in params?

I think this can be a solution:

function getQueryString(params) {
    return Object
    .keys(params)
    .map(k => {
        if (Array.isArray(params[k])) {
            return params[k]
                .map(val => `${encodeURIComponent(k)}[]=${encodeURIComponent(val)}`)
                .join('&')
        }

        return `${encodeURIComponent(k)}=${encodeURIComponent(params[k])}`
    })
    .join('&')
}
@grebenyuksv

This comment has been minimized.

grebenyuksv commented Jan 13, 2017

@od0

This comment has been minimized.

od0 commented Jan 15, 2017

I recommend this snippet from https://fetch.spec.whatwg.org/:

var url = new URL("https://geo.example.org/api"),
    params = {lat:35.696233, long:139.570431}
Object.keys(params).forEach(key => url.searchParams.append(key, params[key]))
fetch(url).then(/**/)

Example function with optional params argument:

const suchFetch = (path, fetchOpts, params) => {
  var url = new URL(`${BASE_URL}${path}`)
  if (params != null) Object.keys(params).forEach(key => url.searchParams.append(key, params[key]))
  return fetch(url, fetchOpts)
    .then((res) => res.json())
    .catch((ex) => console.log("Fetch Exception", ex));
};
@mislav

This comment has been minimized.

Member

mislav commented Jan 16, 2017

Our polyfill right now may handle URL instances incorrectly when passed to fetch() like in example above, but I'm making a fix to improve that.

@AaronHarris

This comment has been minimized.

AaronHarris commented Jan 27, 2017

What about instead of using a JSON object, one could use an instance of URLSearchParams? Wouldn't that work within the spec since it's supported as a body parameter?

@mislav

This comment has been minimized.

Member

mislav commented Jan 27, 2017

@AaronHarris Yes, you can use an instance of URLSearchParams as a POST body. However, the discussion in this thread was about supplying query parameters within the URL for a GET request. GET requests can't have have bodies.

@boutell

This comment has been minimized.

boutell commented Jan 28, 2017

But URLSearchParams has a toString method, so you ought to be able to build a URL cheaply with that.

I don't know if there's a polyfill for that, or if it supports nested structures at all. You might be happier using the qs module.

@monkindey

This comment has been minimized.

monkindey commented Feb 23, 2017

@hbrls awesome workaround, but May we use the ES6 template strings instead of the '+' operator. haha

var params = {
    parameter1: 'value_1',
    parameter2: 'value 2',
    parameter3: 'value&3' 
};

var esc = encodeURIComponent;
var query = Object.keys(params)
    .map(k => `${esc(k)}=${esc(params[k])}`)
    .join('&');
@kouhin

This comment has been minimized.

kouhin commented Mar 30, 2017

I just created a small lib for it.

https://www.npmjs.com/package/with-query

You can just do it like this.

const withQuery = require('with-query');

fetch(withQuery('https://api.github.com/search/repositories', {
  q: 'query',
  sort: 'stars',
  order: 'asc',
}))
.then(res => res.json())
.then((json) => {
  console.info(json);
})
.catch((err) => {
  console.error(err);
});

This library parse and stringify the query string with full power of qs.

@ghost

This comment has been minimized.

ghost commented Mar 30, 2017

If it's a wrapper for qs, why not to use it directly?

@kouhin

This comment has been minimized.

kouhin commented Mar 30, 2017

@dkfiresky Absolutely not!
It uses qs only for parse and stringify the query string. It can also help you to dual with the original url that already has the query string and hash.

e.g.

// You may use a default query parameter in original url. 
// `query` could be string or object
function searchRespositories(query) {
  return fetch(withQuery('https://api.github.com/search/repositories?order=desc', query))
  .then(res => res.json())
  .then((json) => {
    console.info(json);
  })
  .catch((err) => {
    console.error(err);
  });
}
searchRespositories({
  q: 'query',
  sort: 'stars',
  order: 'asc',
})

Actually, out target is using this library in our project. The current code is

function getUserItems(req, options) {
  const url = URL.format({
    ...URL.parse(`${baseURL}/v1.0/${userId}/items/${itemId}`),
    query: options,
  });
  return fetch(url, {
    headers: getRequiredHeaders(req),
  });
}

After

function getUserItems(req, options) {
  return fetch(withQuery(`${baseURL}/v1.0/${userId}/items/${itemId}`, options), {
    headers: getRequiredHeaders(req),
  });
}
@kellyrmilligan

This comment has been minimized.

kellyrmilligan commented Apr 12, 2017

just to chime in, I did this with this setup

import queryString from 'query-string'

fetch(`/some/url/path/?${queryString.stringify(params)}`)
@noinkling

This comment has been minimized.

noinkling commented May 26, 2017

This works in Chrome 58, haven't tested other versions/browsers:

const params = { a: 'foo', b: 'bar' };
const urlParams = new URLSearchParams(Object.entries(params));
fetch('/some/url?' + urlParams);  // can use .toString() if you want to be explicit

Edit: can someone explain the downvotes? It's not an ideal solution, sure, but it uses a standard API, doesn't rely on an external library, and is essentially the same as what @od0 posted above, just a little terser and doesn't require an absolute URL like a URL object does. Browser compatibility is currently reasonable and it has polyfills available. It's also available in Node (very useful, for instance, if you need server+browser rendering support using isomorphic-fetch). Object.entries() is also pretty new, but again, compatibility isn't too bad (check here or here) and it's easily polyfillable.

Yes, query-string has a nicer API and it would be nice for fetch to have something built-in, all I'm doing is suggesting a built-in alternative for people that would prefer to stick to standards. If fetch ever does gain this functionality, URLSearchParams is probably what it will utilize.

@xiaoshuai

This comment has been minimized.

xiaoshuai commented Jul 20, 2017

// refer to above answers

const withQuery = (url, params) => {
  let query = Object.keys(params)
    .filter(k => params[k] !== undefined)
    .map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k]))
    .join('&');
  url += (url.indexOf('?') === -1 ? '?' : '&') + query;
  return url;
};
export default withQuery;

@zhouzhongyuan zhouzhongyuan referenced this issue Jul 26, 2017

Open

dairy #52

0 of 3 tasks complete
@lolmaus

This comment has been minimized.

lolmaus commented Jul 28, 2017

Edit: can someone explain the downvotes?

@noinkling It does not work with nested objects:

new URLSearchParams(Object.entries({foo: {bar: 'baz'}})).toString()
// => "foo=%5Bobject+Object%5D"
@noinkling

This comment has been minimized.

noinkling commented Jul 28, 2017

@lolmaus That's a legitimate concern, but the majority of the solutions suggested in this thread have the same issue because they're just covering the basic use case. Query string serialization of nested structures is generally considered an "advanced" feature (and there are are at least a few ways it can be done) - if it's needed, then a library is indeed likely the best bet (or just write your own function).

URLSearchParams does support values with the same key though, which could be used to the same effect with a bit more work.

@ashic

This comment has been minimized.

ashic commented Aug 31, 2017

I'm using https://medialize.github.io/URI.js , and am very happy with it.

@tfwright

This comment has been minimized.

tfwright commented Nov 21, 2017

Tried using the query-string package initially but it didn't seem to support nested objects, so switched to qs. That's working great so far.

@sjatkins

This comment has been minimized.

sjatkins commented Apr 6, 2018

I can't believe anyone would pretend fetch is a good standard for querying a backend without support for query parameters on GET. Why on earth wasn't this part of the standard from the beginning?

@mislav

This comment has been minimized.

Member

mislav commented Apr 6, 2018

@sjatkins You can propose your feature ideas on the repo of the official spec (we're just a polyfill), but with url.searchParams being the preferred solution of spec authors, and because there is no unified standard of how an encoding mechanism of URI query parameters should work w.r.t. multiple values and nested keys, I don't think the whatwg-fetch spec is going to encompass an API for specifying query params anytime soon.

// https://fetch.spec.whatwg.org/#fetch-api
var url = new URL("https://geo.example.org/api"),
    params = {lat:35.696233, long:139.570431}
Object.keys(params).forEach(key => url.searchParams.append(key, params[key]))
fetch(url).then(...)

Thank you everyone for sharing their solutions!

@github github locked as resolved and limited conversation to collaborators Apr 6, 2018

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.