Retrofit - Change BaseUrl

I have a scenario where I have to call an API with the same base URL, e.g. www.myAPI.com but with a different baseUrl.

I have an instance of Retrofit 2 which is built via a Builder:

return new Retrofit.Builder()
    .baseUrl(FlavourConstants.BASE_URL)
    .addConverterFactory(GsonConverterFactory.create(gson))
    .client(okHttpClient)
    .build();

The FlavourConstants.BASE_URL looks like this:

public static final String BASE_URL = "http://myApi.development:5000/api/v1/";

For some WebRequests, I must call the same API but on others, I must call it from a completely different BaseUrl. How do I change the Retrofit instance to therefore point to a different URL during runtime?

The Retrofit instance doesn't have a .setBaseUrl or setter or anything similar as it's built via a Builder.

Any ideas?


Lucky for you Retrofit have a simple solution for that:

public interface UserManager {  
    @GET
    public Call<ResponseBody> userName(@Url String url);
}

The url String should specify the full Url you wish to use.


Retrofit 2.4, MAY 2019

Two simple solution for this hassle are:

  1. Hardcode the new URL, while leaving the base URL as it is:

    @GET("http://example.com/api/")
    Call<JSONObject> callMethodName();
    
  2. Pass the new URL as an argument, while leaving the base URL as it is:

    @GET
    Call<JSONObject> callMethodName(@Url String url);
    

N.B: These methods work for GET or POST. However, this solution is only efficient if you just need to use an exception of one or two different URLs than your base URL. Otherwise, things can get a little messy in terms of code neatness.

If your project demands fully dynamically generated base URLs then you can start reading this.


Also there is a such hack in Kotlin while defining base url

e.g.

@FormUrlEncoded
@Headers("Accept: application/json")
@POST
suspend fun login(
    baseUrl: String,
    @Field("login") login: String,
    @Field("password") password: String
    @Url url: String = "$baseUrl/auth"
): ResponseAuth

It's not working. Throws:

java.lang.IllegalArgumentException: No Retrofit annotation found. (parameter #1)

The only way is suggested by Jake Wharton https://github.com/square/retrofit/issues/2161#issuecomment-274204152

Retrofit.Builder()
    .baseUrl("https://localhost/")
    .create(ServerApi::class.java)
class DomainInterceptor : Interceptor {

    @Throws(Exception::class)
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        return chain.proceed(
            request.newBuilder()
                .url(
                    request.url.toString()
                        .replace("localhost", "yourdomain.com:443")
                        .toHttpUrlOrNull() ?: request.url
                )
                // OR
                //.url(HttpUrl.parse(request.url().toString().replace("localhost", "yourdomain.com:443")) ?: request.url())
                .build()
        )
    }
}

The easiest (but not the most performant) way to change the Retrofit2 base URL at runtime is to rebuild the retrofit instance with the new url:

private Retrofit retrofitInstance = Retrofit.Builder().baseUrl(FlavourConstants.BASE_URL).addConverterFactory(GsonConverterFactory.create(gson)).client(okHttpClient).build();

public void setNewBaseUrl(String url) {
   retrofitInstance = new Retrofit.Builder()
      .baseUrl(url)
      .addConverterFactory(GsonConverterFactory.create(gson))
      .client(okHttpClient).build();
}

...
retrofitInstance.create(ApiService.class);

Alternatively, if you are using OkHttp with Retrofit, you can add an OkHttp interceptor like this one when building your OkHttp client:

HostSelectionInterceptor hostInterceptor = new HostSelectionInterceptor();
hostInterceptor.setHost(newBaseUrl);
return new OkHttpClient.Builder()
    .addInterceptor(hostInterceptor)
    .build();