Skip to main content

ApiService

Abstract base class for remote data sources. Provides HTTP methods that automatically wrap responses in Resource<T>.

Module: :network

Definition

abstract class ApiService(protected val httpClient: HttpClient)

HTTP Methods

GET

suspend fun getUsers(): Resource<ApiResponse<List<UserDto>>> =
get("users")

suspend fun getUser(id: Int): Resource<ApiResponse<UserDto>> =
get("users/$id")

suspend fun searchUsers(query: String): Resource<ApiResponse<List<UserDto>>> =
get("users/search", params = mapOf("q" to query))

POST

suspend fun createUser(request: CreateUserRequest): Resource<ApiResponse<UserDto>> =
post("users", body = request)

PUT

suspend fun updateUser(id: Int, request: UpdateUserRequest): Resource<ApiResponse<UserDto>> =
put("users/$id", body = request)

PATCH

suspend fun patchUser(id: Int, request: PatchUserRequest): Resource<ApiResponse<UserDto>> =
patch("users/$id", body = request)

DELETE

suspend fun deleteUser(id: Int): Resource<ApiResponse<Unit>> =
delete("users/$id")

Method Signatures

MethodSignature
getsuspend fun get<T>(path: String, params: Map<String, String>? = null): Resource<T>
postsuspend fun post<T>(path: String, body: Any? = null, params: Map<String, String>? = null): Resource<T>
putsuspend fun put<T>(path: String, body: Any? = null, params: Map<String, String>? = null): Resource<T>
patchsuspend fun patch<T>(path: String, body: Any? = null, params: Map<String, String>? = null): Resource<T>
deletesuspend fun delete<T>(path: String, params: Map<String, String>? = null): Resource<T>

Error Handling

All methods internally use safeApiCall which catches exceptions and maps them to typed AppException:

  • HTTP 401 → AppException.UnauthorizedException
  • HTTP 403 → AppException.ForbiddenException
  • HTTP 404 → AppException.NotFoundException
  • HTTP 5xx → AppException.ServerException(code)
  • Timeout → AppException.TimeoutException
  • No network → AppException.NetworkException
  • Other → AppException.UnknownException

Complete Example

// Data source (data layer)
class ProductApi(httpClient: HttpClient) : ApiService(httpClient) {
suspend fun getProducts(): Resource<ApiResponse<List<ProductDto>>> =
get("products")
suspend fun getProduct(id: Int): Resource<ApiResponse<ProductDto>> =
get("products/$id")
suspend fun createProduct(body: CreateProductRequest): Resource<ApiResponse<ProductDto>> =
post("products", body = body)
}

// Repository implementation (data layer)
class ProductRepositoryImpl(
private val api: ProductApi,
private val mapper: ProductMapper
) : ProductRepository {

override suspend fun getProducts(): Resource<List<Product>> =
api.getProducts().map { response ->
response.data?.map { mapper.map(it) } ?: emptyList()
}
}