'Idiom to construct a URI with a query string in Elixir

I'm wondering what the most idiomatic way is to use URI to add a query string to a base URI in Elixir.

I'm currently doing something like this:

iex(1)> base = "http://example.com/endpoint"
"http://example.com/endpoint"

iex(2)> query_string = URI.encode_query(foo: "bar")
"foo=bar"

iex(3)> uri_string = URI.parse(base) |> Map.put(:query, query_string) |> URI.to_string
"http://example.com/endpoint?foo=bar"

But was wondering if there is a cleaner way to set the query string. I know about URI.merge/2, but I don't think a query string is a valid URI, so that's probably not approriate here (the ? will not be added).

I could also do something like:

uri_string = %URI{ URI.parse(base) | query: query_string } |> URI.to_string

But I'm wondering if there is a simpler or clearer method I've missed.



Solution 1:[1]

I think your first example, with some formatting, reads pretty well:

uri_string =
  base_url
  |> URI.parse()
  |> Map.put(:query, URI.encode_query(params))
  |> URI.to_string()

I'm not aware of other idioms/shortcuts.

Solution 2:[2]

I'm not sure that it's more idiomatic but you can do simple string interpolation if you know the string you're trying to build.

uri_string = "#{base}?#{query_string}"

Solution 3:[3]

As of Elixir 1.13, URI.new!/1 is available, which unlike URI.parse/1 also validates the URI.

It's also safer to use Map.replace!/3 instead of Map.put/3, to ensure you do not make a typo of the query key of the URI struct.

"http://example.com/endpoint"
|> URI.new!()
|> Map.replace!(:query, URI.encode_query(foo: "bar"))
|> URI.to_string()

You can also use then/2 (added in Elixir 1.12) to make use of struct sematics if you prefer. The benefit of this is that you will get a compile time, rather than a runtime error if you get the query key wrong.

"http://example.com/endpoint"
|> URI.new!()
|> then(&%URI{&1 | query: URI.encode_query(foo: "bar")})
|> URI.to_string()

Output for both examples:

"http://example.com/endpoint?foo=bar"

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 zwippie
Solution 2 Ronan Boiteau
Solution 3