A repository of bitesize articles, tips & tricks
(in both English and French) curated by Mirego’s team.

Elixir’s URI.to_string/1 or how I stopped worrying about default ports

We had some code that would build a URI using three parts: a scheme, a host and a port. The port should not be included if it’s 80 because we might want to build http://example.com:8080 but never http://example.com:80 (http://example.com is much nicer even though it’s the same thing).

case port do
  80 -> "#{scheme}://#{host}"
  port -> "#{scheme}://#{host}:#{port}"
end

I’m not a fan of string interpolation with arbitrary values and literals (eg. ://) so I knew there’s had to be a better way to build URIs with Elixir. URI.to_string/1 is the way.

%URI{host: "example.com", scheme: "http"} |> URI.to_string()
"http://example.com"

%URI{host: "example.com", scheme: "http", port: 80} |> URI.to_string()
"http://example.com"

%URI{host: "example.com", scheme: "http", port: 81} |> URI.to_string()
"http://example.com:81"

%URI{host: "example.com", scheme: "http", port: 443} |> URI.to_string()
"http://example.com:443"

%URI{host: "example.com", scheme: "https", port: 443} |> URI.to_string()
"https://example.com"

And turns out, URI.to_string/1 uses URI.default_port/1 to figure out if the port has to be explicitely included in the string representation 🎉 so we don’t need to worry about it anymore!

And just for fun, here are the default ports known by Elixir 1.13:

URIConfig = [
  {{uri, <<"ftp">>}, 21},
  {{uri, <<"sftp">>}, 22},
  {{uri, <<"tftp">>}, 69},
  {{uri, <<"http">>}, 80},
  {{uri, <<"https">>}, 443},
  {{uri, <<"ldap">>}, 389}
]