6

I'm using Fetch (Fetch API) in a project and I would like to, for consistence purposes, create a function that receives all the parameters such as method, url and data and creates the correct request, depending if it's a GET or a POST request.

Is it possible, using Fetch, to send a data object that for the GET request, converts data into and string with the parameters and if it is a POST request, it just sends the data object in the body?

It would look like this:

fetch ('/test', {
        method: 'GET',
        data: {
           test: 'test'
        }
});

This doubt was inspired by this jQuery ajax behaviour:

$.ajax({
   url: '/test',
   method: 'GET',
   data: {
      test: 'test'
   }
});

This would produce this request:

'/test/?test=test'
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Defesa Esquerdo
  • 290
  • 2
  • 3
  • 10
  • 1
    Well, yes, this is possible. But what is your question exactly? (note: you are aware of the fact that IE and safari don't support this?) – giorgio Aug 31 '16 at 09:40
  • Yes, I'm aware of that, it's just for an experiment :) My question is: If I pass the data object as normal in the fetch constructor for a GET request, would it send the request like the example I gave '/test/?test=test' ? – Defesa Esquerdo Aug 31 '16 at 12:12
  • 3
    [Did you try it](https://i.imgur.com/buUeJ0c.jpg) ? – Royi Namir Aug 31 '16 at 12:21
  • I tried it but I was more interested in the iteration through the multiple properties in the data object. Besides I searched for this question here and I couldn't find nothing, so I decided to create a topic that might be useful to other developers. Thank you! :) – Defesa Esquerdo Aug 31 '16 at 14:10

3 Answers3

10

If I pass the data object as normal in the fetch constructor for a GET request, would it send the request like the example I gave '/test/?test=test'

If you want to add query string to a fetch request :

From the SPEC

var url = new URL("https://a.com/method"),
params = {a:1, b:2}
Object.keys(params).forEach(key => url.searchParams.append(key, params[key]))
fetch(url)

this will produce a request :

enter image description here

Royi Namir
  • 144,742
  • 138
  • 468
  • 792
2

you could either use the Url class:

var url = new URL("/test/")
Object.keys({test: 'test', a: 1}).forEach(key => url.searchParams.append(key, params[key]))
fetch(url);

or parse the string yourself, if you want wider browser support:

var params = {test: 'test', a: 1},
    qs = Object.keys(params).reduce(function(_qs, k, i){ return _qs + '&' + k + '=' + params[k]; }, '').substring(1);

console.log(qs)
MoLow
  • 3,056
  • 2
  • 21
  • 41
0

I'll show you snippets for creating query with and without using URLSearchParams.

The code will be in typescript for the if you're using it. if not, just remove types, it will work in the same way.

Simple solution (URLSearchParams)

/**
 * Creates query from given object
 * - It doesn't work with deep nesting
 * - It doesn't remove empty fields
 * @returns `state1=6&state2=horse` without `?`
 */
function createQuery(queryObject?: Record<string | number, unknown> | null): string {
  if (queryObject == null) return ""
  // Typescript: The `as ...` expression here is ok because `URLSearchParams` will convert non-string by itself
  const searchParams = new URLSearchParams(queryObject as Record<string, string>)
  return searchParams.toString()
}

Solving problems solution (URLSearchParams)

/**
 * Creates query from given object
 * - It doesn't work with deep nesting
 * - Removes empty fields
 * @returns `state1=6&state2=horse` without `?`
 */
function createQuery(queryObject?: Record<string | number, unknown> | null): string {
  if (queryObject == null || !Object.keys(queryObject).length) return ""
  for (const key in queryObject) {
    if (Object.prototype.hasOwnProperty.call(queryObject, key)) {
      const value = queryObject[key]
      // Use `!value` expression if you want to delete values as `0` (zero) and `""` (empty string) too.
      if (value == null) delete queryObject[key]
    }
  }
  const searchParams = new URLSearchParams(queryObject as Record<string, string>)
  return searchParams.toString()
}

No URLSearchParams solution

/**
 * Creates query from given object
 * - Supports prefixes
 * - Supports deep nesting
 * - Removes empty fields
 * @returns `state1=6&state2=horse` without `?`
 */
function createQuery(queryObject?: Record<string | number, unknown> | null, keyPrefix?: string): string {
  if (queryObject == null || !Object.keys(queryObject).length) return ""
  keyPrefix = keyPrefix ? (keyPrefix + "_") : ""

  const queryKeys = Object.keys(queryObject)
  const queryArray = queryKeys.map(key => {
    const value = queryObject[key]
    if (value) {
      if (isDictionary(value)) {
        return createQuery(value, keyPrefix + key + "_")
      }

      return keyPrefix + encodeURIComponent(key) + "=" + encodeURIComponent(String(value))
    }
    return ""
  })

  return queryArray.filter(Boolean).join("&")
}

isDictionary Helper

I used isDictionary helper here too, you can find it here

Usage

You need to put ? in the beginning of your endpoint plus createQuery

fetch("/test?" + createQuery({ foo: 12, bar: "@user->here", object: { test: "test", bird: { super: { ultra: { mega: { deep: "human" }, shop: 7 } }, multiple: [1, 2, 3] } } }))
Result
foo=12&bar=%40user-%3Ehere&object_test=test&object_bird_super_ultra_mega_deep=human&object_bird_super_ultra_shop=7&object_bird_multiple=1%2C2%2C3

or

foo: 12
bar: @user->here
object_test: test
object_bird_super_ultra_mega_deep: human
object_bird_super_ultra_shop: 7
object_bird_multiple: 1,2,3

enter image description here

Conclusion

We got different snippets you can choose from depending on your goals.

FrameMuse
  • 143
  • 1
  • 9