Need to create a body for Tesla with duplicate map keys

Hey all,
I am using Tesla to make a POST request and I need to post a body with duplicate keys, like this map:

%{
  "id[]" => "1",
  "id[]" => "2",
  "id[]" => "3"
}

The problem is that when I try to create this map, it does not keep the duplicated keys id[], so how can I generate this kind of body with a list of duplicated keys?

What I have so far:
I have this map as input:

ids = %{
  "1" => "any-value",
  "2" => "any-value",
  "3" => "any-value"
}

and I am trying to convert that to this output to post as the body in Tesla:

%{
  "id[]" => "1",
  "id[]" => "2",
  "id[]" => "3"
}

with this code:

ids |> Enum.map(fn {id, _} -> %{"id[]" => id} end) |> Enum.reduce(&Map.merge/2)

but as I said this keeps only one id[] in the map, e.g: %{"id[]" => "1"}.

Does anyone have any thoughts on how can I achieve this?

Thanks in advance,
Rafael.

Map data structure can’t have duplicate keys, so using Map to represent POST request body which could have duplicate keys shouldn’t be possible.

Alternatively, it seems Tesla able to support POST body as Keyword List (which are able to have duplicate key), using FormUrlencoded format

So it might be possible to change your request body to

["id[]": "1", "id[]": 2, "id[]": 3]

What’s the format of the body? Seems like you want application/x-www-form-urlencoded, so time to use Tesla.Middleware.FormUrlencoded, which uses URI.encode_query/1 under the hood.

    client = Tesla.client([
      {Tesla.Middleware.BaseUrl, "https://httpbin.org/"},
      Tesla.Middleware.EncodeFormUrlencoded,
      Tesla.Middleware.DecodeJson,
      Tesla.Middleware.Logger
    ])
    
    client |> Tesla.post("/post", [{"id[]", "1"},{"id[]", "2"}])

Output excerpt:

>>> REQUEST >>>
(no query)
content-type: application/x-www-form-urlencoded

id%5B%5D=1&id%5B%5D=2

<<< RESPONSE <<<

{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "id[]": [
      "1",
      "2"
    ]
  },
  "headers": {
    "Content-Length": "21",
    "Content-Type": "application/x-www-form-urlencoded",
    "Host": "httpbin.org",
  },
  "json": null,
  "url": "https://httpbin.org/post"
}

Please note

  • [] notation to take values into array is not a standard but just common convention. e.g. See Plug.Conn.Query
  • Elixir’s URI.decode_query returns a map, which is convenient but it’s wrong to use parse a string in urlencoded form, since WHATWG defines it as list of key/value, not map!

For that exact reason I wrote whatwg/www_form_urlencoded.ex at main · chulkilee/whatwg · GitHub a while ago.

1 Like