package com.example.github;
import java.util.List;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Path;
import retrofit2.http.Query;
public interface GitHubApi {
@GET("user")
Call<User> getAuthenticatedUser();
@GET("users/{user}/repos")
Call<List<Repository>> getRepositories(
@Path("user") String username,
@Query("sort") String sort);
}
package com.example.github;
import java.io.IOException;
import java.util.function.Supplier;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
public final class AuthInterceptor implements Interceptor {
private final Supplier<String> tokenSupplier;
public AuthInterceptor(Supplier<String> tokenSupplier) {
this.tokenSupplier = tokenSupplier;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
String token = tokenSupplier.get();
if (token == null || token.isBlank()) {
return chain.proceed(original);
}
Request authorized = original.newBuilder()
.header("Authorization", "Bearer " + token)
.header("Accept", "application/vnd.github+json")
.build();
return chain.proceed(authorized);
}
}
package com.example.github;
import java.util.function.Supplier;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public final class GitHubClientFactory {
private static final String BASE_URL = "https://api.github.com/";
private GitHubClientFactory() {
}
public static GitHubApi create(Supplier<String> tokenSupplier) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
OkHttpClient httpClient = new OkHttpClient.Builder()
.addInterceptor(new AuthInterceptor(tokenSupplier))
.addInterceptor(logging)
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(httpClient)
.addConverterFactory(GsonConverterFactory.create())
.build();
return retrofit.create(GitHubApi.class);
}
}
This snippet shows the standard way to build a typed, declarative HTTP client on the JVM using Retrofit for the interface and OkHttp for the transport layer. The idea behind a declarative client is that endpoints are described as annotated Java method signatures rather than hand-written request-building code. Retrofit generates the actual implementation at runtime, converting each method call into an HTTP request and each response body into a typed model via a converter such as Gson.
In GitHubApi, the interface declares two endpoints. @GET("user") maps to a fixed path, while getRepositories uses @Path to substitute a value into users/{user}/repos and @Query to append sort as a query parameter. The return type Call<List<Repository>> is what makes the client typed: the compiler knows the exact shape of the response, and Retrofit handles deserialization. Because the method signature is the contract, this eliminates an entire class of string-concatenation and parsing bugs.
AuthInterceptor implements OkHttp's Interceptor interface, the correct place for cross-cutting request concerns. Interceptors form a chain, and this one rewrites every outgoing request via chain.request().newBuilder() to attach an Authorization header and a JSON Accept header. Requests in OkHttp are immutable, so the pattern is always to derive a new request with newBuilder() rather than mutate. The token is supplied lazily through a Supplier<String>, which decouples the interceptor from token storage and allows the credential to change over time — important once refresh logic is added. A guard skips the header when the token is blank so unauthenticated calls still work.
GitHubClientFactory wires everything together. It builds an OkHttpClient with the interceptor plus a HttpLoggingInterceptor set to BASIC, then hands that client to Retrofit.Builder. Ordering matters: the auth interceptor is added before logging so the log reflects the final request. The GsonConverterFactory connects JSON deserialization to the typed return values. Retrofit's create produces the concrete GitHubApi proxy.
A key trade-off is that Call is synchronous-or-async but not reactive; teams wanting coroutines or CompletableFuture swap the return type and adapter. Interceptors also run on every request, so expensive work there affects all traffic — token refresh in particular should be handled with an Authenticator for 401s rather than blocking inside the interceptor.
Related snips
payload = {
sub: user.id,
iss: 'https://auth.example.com',
aud: 'codesnips-api',
exp: 15.minutes.from_now.to_i,
iat: Time.now.to_i,
JWT issuance and verification without common footguns
export interface RetryOptions {
retries: number;
baseMs: number;
maxMs: number;
signal?: AbortSignal;
onRetry?: (attempt: number, delay: number, err: unknown) => void;
Exponential backoff with jitter for retries
package com.example.myapp
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
Dependency injection with Hilt
package com.example.myapp.ui
import androidx.lifecycle.*
import kotlinx.coroutines.launch
class UserViewModel(
LiveData transformations and mediators
import { randomBytes, createHash } from "crypto";
import jwt from "jsonwebtoken";
import { RefreshTokenStore } from "./store";
const ACCESS_SECRET = process.env.ACCESS_SECRET!;
const ACCESS_TTL = "15m";
JWT access + refresh token rotation (conceptual)
package com.example.myapp.utils
import android.os.Build
import android.os.StrictMode
import android.os.Trace
import timber.log.Timber
Performance optimization and profiling
Share this code
Here's the card — post it anywhere.