Retrofit — Multiples base urls with annotations

ThinkUp
3 min readJun 29, 2022

When we create a new application, we will probably have multiple API calls. And probably some of them will be at different URLs.

Retrofit provides a Url annotation to use in our methods, but we don’t want to manage the string, and we don’t want to pass it on every invocation of the method.

We want each service to know which URL it should point to. Well, in this tutorial, we are going to see a solution:

ServiceFactory

You may be using a factory to create services instances. Something like this:

class ServiceFactory() {
private val defaultApiUrl = "https://www.example.com/v1/"
fun <T> createInstance(clazz: Class<T>): T {
return retrofit(defaultApiUrl).create(clazz)
}
private fun retrofit(apiUrl: String) = Retrofit.Builder()
.baseUrl(apiUrl)
.client(okHttpClient())
.addConverterFactory(GsonConverterFactory.create())
.build()
private fun okHttpClient(): OkHttpClient = OkHttpClient.Builder().build()
}

But… how can we change the base url dynamically?

Well, to do that we need to change the «ServiceFactory» class to take the value of «defaultApiUrl» depending on the service we are looking to create.
To achieve that we will create a new annotation class called «ApiUrl».

@Target(AnnotationTarget.CLASS)
annotation class ApiUrl(
val url: String
)

We will apply this class to retrofit interfaces as class annotation.

Using Annotations

Now is the time to see the change in the ServiceFactory class. Let’s start by asking if our service contains the annotation we just creatred. In order to do that, in the «createInstance» method, we will add the following line to the beginning:

val apiUrlAnnotation = clazz.annotations.find { it is ApiUrl } as ApiUrl?

If it does, let’s use the value for the new instance. Otherwise we will use the default defined in «ServiceFactory».

How do we use it in a service? Easy, just add the annotation to the class indicating the value of the url.

@ApiUrl(url = "https://www.somthingelse.com/v1/")
interface VideoService {
// more code
}

And that’s it! Now we can change the base url of Retrofit without having to differentiate or create two different paths. Let’s see a complete example.

Example

class ServiceFactory() {
private val defaultApiUrl = "https://www.example.com/v1/"
fun <T> createInstance(clazz: Class<T>): T {
// Check if the service have an annotation
val apiUrlAnnotation = clazz.annotations.find { it is ApiUrl } as ApiUrl?
// Take the url value, in another hand use the default
val url = apiUrlAnnotation?.url ?: defaultApiUrl
// And finally create the service using de extracted url
return retrofit(url).create(clazz)
}
private fun retrofit(apiUrl: String) = Retrofit.Builder()
.baseUrl(apiUrl)
.client(okHttpClient())
.addConverterFactory(GsonConverterFactory.create())
.build()
private fun okHttpClient(): OkHttpClient = OkHttpClient.Builder().build()
}

An extra improvement

Instead of using urls in your code, compile them into the BuildConfig class.
In this way you can save your urls in the gradle.properties file and refer to them in the build.config file.

gradle.properties

base_url="https://www.example.com/v1/"
other_url="https://www.somthingelse.com/v1/"

build.gradle

buildConfigField('String', 'API_URL', "$base_url")
buildConfigField('String', 'OTHER_URL', "$other_url")

and finally use it like that:

// In a service class
@ApiUrl(url = BuildConfig.OTHER_URL)
// In the service faactory class
private val defaultApiUrl = BuildConfig.API_URL

Do you want to develop your next AR project with us? Contact us!

--

--