
Simplifies REST API communication and WebSocket management, offering unified network request handling, token-based authentication, localization, and advanced error handling with minimal setup.
A powerful, easy-to-use Kotlin Multiplatform networking library that simplifies REST API communication and WebSocket management with minimal setup.
Add to your build.gradle.kts:
dependencies {
implementation("global.deveng:networking-kmp:latest")
}// 1. Configure the module
val config = DevengNetworkingConfig(
loggingEnabled = true, // Optional - defaults to true
requestTimeoutMillis = 30_000L, // Optional - defaults to 60 seconds
connectTimeoutMillis = 10_000L, // Optional - defaults to 10 seconds
socketTimeoutMillis = 30_000L, // Optional - defaults to 60 seconds
token = "your-auth-token", // Optional - for authentication
locale = Locale.EN, // Optional - defaults to EN
customHeaders = mapOf( // Optional - for global headers
"X-API-Version" to "1.0",
"X-Client-Platform" to "Android"
),
socketBaseUrl = "wss://ws.example.com" // Optional - only needed for WebSocket
)
// 2. Initialize
DevengNetworkingModule.initDevengNetworkingModule(
restBaseUrl = "https://api.example.com", // Required - your REST API base URL
config = config
)
// 3. Make your first API call
suspend fun getUser(userId: String): User? {
return DevengNetworkingModule.sendRequest<Unit, User?>(
endpoint = "/users/{userId}",
requestMethod = DevengHttpMethod.GET,
pathParameters = mapOf("userId" to userId)
)
}// For Java < 21, use this approach:
suspend fun getUser(userId: String): User {
return DevengNetworkingModule.sendRequest(
endpoint = "/users/{userId}",
requestMethod = DevengHttpMethod.GET,
pathParameters = mapOf("userId" to userId),
requestSerializer = null, // No request body
responseSerializer = User.serializer() // Explicit response serializer
)
}| Feature | Description |
|---|---|
| π Full REST API Support | GET, POST, PUT, DELETE with automatic serialization |
| π File Upload Support | Simple multipart file uploads with MIME detection |
| π WebSocket Management | Connection pooling, lifecycle events, automatic reconnection |
| π― Multiplatform | Android, iOS, Desktop (JVM), WebAssembly |
| π Authentication | Built-in token-based authentication |
| π Localization | Localized error messages and headers |
| π Error Handling | Centralized, customizable exception handling |
| π οΈ Dynamic Parameters | Path and query parameter injection |
| π Custom Headers | Global and per-request custom headers |
| π§ Configurable Logging | Enable/disable logging per environment |
| β±οΈ Timeout Control | Request, connection, and socket timeouts |
// Development
val devConfig = DevengNetworkingConfig(
loggingEnabled = true,
requestTimeoutMillis = 120_000L,
token = "dev-token"
)
// Production
val prodConfig = DevengNetworkingConfig(
loggingEnabled = false,
requestTimeoutMillis = 30_000L,
customHeaders = mapOf("X-Client-Version" to "2.1.0")
)// GET Request
suspend fun getUser(id: String): User? {
return DevengNetworkingModule.sendRequest<Unit, User?>(
endpoint = "/users/{id}",
requestMethod = DevengHttpMethod.GET,
pathParameters = mapOf("id" to id)
)
}
// POST Request
suspend fun createUser(user: CreateUserRequest): User? {
return DevengNetworkingModule.sendRequest<CreateUserRequest, User?>(
endpoint = "/users",
requestBody = user,
requestMethod = DevengHttpMethod.POST
)
}
// PUT Request
suspend fun updateUser(id: String, user: UpdateUserRequest) {
DevengNetworkingModule.sendRequest<UpdateUserRequest, Unit>(
endpoint = "/users/{id}",
requestBody = user,
requestMethod = DevengHttpMethod.PUT,
pathParameters = mapOf("id" to id)
)
}
// DELETE Request
suspend fun deleteUser(id: String) {
DevengNetworkingModule.sendRequest<Unit, Unit>(
endpoint = "/users/{id}",
requestMethod = DevengHttpMethod.DELETE,
pathParameters = mapOf("id" to id)
)
}suspend fun searchUsers(query: String, limit: Int): List<User>? {
return DevengNetworkingModule.sendRequest<Unit, List<User>?>(
endpoint = "/users/search",
requestMethod = DevengHttpMethod.GET,
queryParameters = mapOf(
"query" to query,
"limit" to limit.toString()
)
)
}Use this when you need access to HTTP headers, status codes, or other response metadata:
suspend fun getRawResponse(): HttpResponse {
return DevengNetworkingModule.sendRequestForHttpResponse<Unit>(
endpoint = "/status",
requestMethod = DevengHttpMethod.GET
)
}
// Example usage - accessing response metadata
suspend fun checkApiLimits() {
val response = DevengNetworkingModule.sendRequestForHttpResponse<Unit>(
endpoint = "/api/limits",
requestMethod = DevengHttpMethod.GET
)
// Access headers for rate limiting info
val remainingRequests = response.headers["X-RateLimit-Remaining"]
val resetTime = response.headers["X-RateLimit-Reset"]
// Check exact status code
when (response.status.value) {
200 -> println("API healthy")
429 -> println("Rate limited - retry after $resetTime")
else -> println("Status: ${response.status}")
}
}Common use cases:
X-RateLimit-* headersLink or pagination headersContent-Type, Content-Length, etc.Upload files with multipart form data using the same simplicity as regular API calls with DevengHttpMethod.MULTIPART:
suspend fun uploadProfilePicture(userId: String, imageData: ByteArray) {
DevengNetworkingModule.sendRequest<Unit, Unit>(
endpoint = "/users/$userId/avatar",
requestMethod = DevengHttpMethod.MULTIPART,
fileName = "profile.jpg",
fileContent = imageData
)
}suspend fun uploadDocument(
fileName: String,
fileData: ByteArray,
documentType: String,
isPublic: Boolean
) {
DevengNetworkingModule.sendRequest<Unit, UploadResponse>(
endpoint = "/documents/upload",
requestMethod = DevengHttpMethod.MULTIPART,
fileName = fileName,
fileContent = fileData,
additionalFormFields = mapOf(
"type" to documentType,
"isPublic" to isPublic.toString(),
"uploadedAt" to System.currentTimeMillis().toString()
)
)
}suspend fun uploadAttachment(fileName: String, fileData: ByteArray) {
DevengNetworkingModule.sendRequest<Unit, Unit>(
endpoint = "/attachments",
requestMethod = DevengHttpMethod.MULTIPART,
fileName = fileName,
fileContent = fileData,
fileFieldName = "attachment" // Custom field name instead of default "File"
)
}data class UploadResponse(val fileId: String, val downloadUrl: String)
suspend fun uploadAndGetUrl(fileName: String, fileData: ByteArray): UploadResponse {
return DevengNetworkingModule.sendRequest<Unit, UploadResponse>(
endpoint = "/files/upload",
requestMethod = DevengHttpMethod.MULTIPART,
fileName = fileName,
fileContent = fileData,
additionalFormFields = mapOf("source" to "mobile")
)
}suspend fun uploadFile(fileName: String, fileData: ByteArray): UploadResponse {
return DevengNetworkingModule.sendRequest(
endpoint = "/files/upload",
requestMethod = DevengHttpMethod.MULTIPART,
requestSerializer = null,
responseSerializer = UploadResponse.serializer(),
fileName = fileName,
fileContent = fileData
)
}Multipart File Upload Features:
val connection = DevengNetworkingModule.connectToWebSocket(
endpoint = "/chat",
onConnected = {
sendMessage("Hello Server!")
},
onMessageReceived = { message ->
println("Received: $message")
},
onError = { error ->
println("WebSocket Error: $error")
},
onClose = {
println("Connection closed")
}
)
// Send messages
connection.sendMessage("Hello World!")scope.launch {
connection.connectionState.collect { state ->
when (state) {
is ConnectionState.Connected -> println("β
Connected")
is ConnectionState.Connecting -> println("π Connecting...")
is ConnectionState.Disconnected -> println("β Disconnected")
is ConnectionState.Error -> println("β οΈ Error: ${state.error}")
}
}
}// Configure max connections (default: 5)
WebSocketConnection.setMaxConnections(3)
// Get connection stats
val activeConnections = WebSocketConnection.getActiveConnections()
val connectionCount = WebSocketConnection.getConnectionCount()
// Close specific connection
DevengNetworkingModule.closeWebSocketConnection("/chat")
// Close all connections
DevengNetworkingModule.closeAllWebSocketConnections()try {
val user = DevengNetworkingModule.sendRequest<Unit, User?>(
endpoint = "/users/123",
requestMethod = DevengHttpMethod.GET
)
} catch (e: DevengException) {
println("Error: ${e.message}") // Localized error message
}class CustomExceptionHandler : ExceptionHandler {
override var locale: Locale = Locale.EN
override fun handleHttpException(
errorMessage: String?,
status: HttpStatusCode
): DevengUiError {
// Custom HTTP error logic
return when (status.value) {
404 -> DevengUiError("Resource not found")
500 -> DevengUiError("Server error occurred")
else -> DevengUiError("Request failed: $errorMessage")
}
}
override fun handleNetworkException(cause: Throwable): DevengUiError {
// Custom network error logic
return DevengUiError("Network connection failed")
}
}// Update authentication token at runtime
DevengNetworkingModule.setToken("new-auth-token")Add to libs.versions.toml:
[versions]
devengNetworkingKmp = "latest"
[libraries]
deveng-networking-kmp = {
module = "global.deveng:networking-kmp",
version.ref = "devengNetworkingKmp"
}Then in build.gradle.kts:
dependencies {
implementation(libs.deveng.networking.kmp)
}The library is built on top of Ktor and provides:
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
We welcome contributions! Please:
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)For major changes, please open an issue first to discuss your proposal.
Created by Deveng Group - Building powerful, developer-friendly tools for the Kotlin ecosystem.
Made with β€οΈ by the Deveng Group team
A powerful, easy-to-use Kotlin Multiplatform networking library that simplifies REST API communication and WebSocket management with minimal setup.
Add to your build.gradle.kts:
dependencies {
implementation("global.deveng:networking-kmp:latest")
}// 1. Configure the module
val config = DevengNetworkingConfig(
loggingEnabled = true, // Optional - defaults to true
requestTimeoutMillis = 30_000L, // Optional - defaults to 60 seconds
connectTimeoutMillis = 10_000L, // Optional - defaults to 10 seconds
socketTimeoutMillis = 30_000L, // Optional - defaults to 60 seconds
token = "your-auth-token", // Optional - for authentication
locale = Locale.EN, // Optional - defaults to EN
customHeaders = mapOf( // Optional - for global headers
"X-API-Version" to "1.0",
"X-Client-Platform" to "Android"
),
socketBaseUrl = "wss://ws.example.com" // Optional - only needed for WebSocket
)
// 2. Initialize
DevengNetworkingModule.initDevengNetworkingModule(
restBaseUrl = "https://api.example.com", // Required - your REST API base URL
config = config
)
// 3. Make your first API call
suspend fun getUser(userId: String): User? {
return DevengNetworkingModule.sendRequest<Unit, User?>(
endpoint = "/users/{userId}",
requestMethod = DevengHttpMethod.GET,
pathParameters = mapOf("userId" to userId)
)
}// For Java < 21, use this approach:
suspend fun getUser(userId: String): User {
return DevengNetworkingModule.sendRequest(
endpoint = "/users/{userId}",
requestMethod = DevengHttpMethod.GET,
pathParameters = mapOf("userId" to userId),
requestSerializer = null, // No request body
responseSerializer = User.serializer() // Explicit response serializer
)
}| Feature | Description |
|---|---|
| π Full REST API Support | GET, POST, PUT, DELETE with automatic serialization |
| π File Upload Support | Simple multipart file uploads with MIME detection |
| π WebSocket Management | Connection pooling, lifecycle events, automatic reconnection |
| π― Multiplatform | Android, iOS, Desktop (JVM), WebAssembly |
| π Authentication | Built-in token-based authentication |
| π Localization | Localized error messages and headers |
| π Error Handling | Centralized, customizable exception handling |
| π οΈ Dynamic Parameters | Path and query parameter injection |
| π Custom Headers | Global and per-request custom headers |
| π§ Configurable Logging | Enable/disable logging per environment |
| β±οΈ Timeout Control | Request, connection, and socket timeouts |
// Development
val devConfig = DevengNetworkingConfig(
loggingEnabled = true,
requestTimeoutMillis = 120_000L,
token = "dev-token"
)
// Production
val prodConfig = DevengNetworkingConfig(
loggingEnabled = false,
requestTimeoutMillis = 30_000L,
customHeaders = mapOf("X-Client-Version" to "2.1.0")
)// GET Request
suspend fun getUser(id: String): User? {
return DevengNetworkingModule.sendRequest<Unit, User?>(
endpoint = "/users/{id}",
requestMethod = DevengHttpMethod.GET,
pathParameters = mapOf("id" to id)
)
}
// POST Request
suspend fun createUser(user: CreateUserRequest): User? {
return DevengNetworkingModule.sendRequest<CreateUserRequest, User?>(
endpoint = "/users",
requestBody = user,
requestMethod = DevengHttpMethod.POST
)
}
// PUT Request
suspend fun updateUser(id: String, user: UpdateUserRequest) {
DevengNetworkingModule.sendRequest<UpdateUserRequest, Unit>(
endpoint = "/users/{id}",
requestBody = user,
requestMethod = DevengHttpMethod.PUT,
pathParameters = mapOf("id" to id)
)
}
// DELETE Request
suspend fun deleteUser(id: String) {
DevengNetworkingModule.sendRequest<Unit, Unit>(
endpoint = "/users/{id}",
requestMethod = DevengHttpMethod.DELETE,
pathParameters = mapOf("id" to id)
)
}suspend fun searchUsers(query: String, limit: Int): List<User>? {
return DevengNetworkingModule.sendRequest<Unit, List<User>?>(
endpoint = "/users/search",
requestMethod = DevengHttpMethod.GET,
queryParameters = mapOf(
"query" to query,
"limit" to limit.toString()
)
)
}Use this when you need access to HTTP headers, status codes, or other response metadata:
suspend fun getRawResponse(): HttpResponse {
return DevengNetworkingModule.sendRequestForHttpResponse<Unit>(
endpoint = "/status",
requestMethod = DevengHttpMethod.GET
)
}
// Example usage - accessing response metadata
suspend fun checkApiLimits() {
val response = DevengNetworkingModule.sendRequestForHttpResponse<Unit>(
endpoint = "/api/limits",
requestMethod = DevengHttpMethod.GET
)
// Access headers for rate limiting info
val remainingRequests = response.headers["X-RateLimit-Remaining"]
val resetTime = response.headers["X-RateLimit-Reset"]
// Check exact status code
when (response.status.value) {
200 -> println("API healthy")
429 -> println("Rate limited - retry after $resetTime")
else -> println("Status: ${response.status}")
}
}Common use cases:
X-RateLimit-* headersLink or pagination headersContent-Type, Content-Length, etc.Upload files with multipart form data using the same simplicity as regular API calls with DevengHttpMethod.MULTIPART:
suspend fun uploadProfilePicture(userId: String, imageData: ByteArray) {
DevengNetworkingModule.sendRequest<Unit, Unit>(
endpoint = "/users/$userId/avatar",
requestMethod = DevengHttpMethod.MULTIPART,
fileName = "profile.jpg",
fileContent = imageData
)
}suspend fun uploadDocument(
fileName: String,
fileData: ByteArray,
documentType: String,
isPublic: Boolean
) {
DevengNetworkingModule.sendRequest<Unit, UploadResponse>(
endpoint = "/documents/upload",
requestMethod = DevengHttpMethod.MULTIPART,
fileName = fileName,
fileContent = fileData,
additionalFormFields = mapOf(
"type" to documentType,
"isPublic" to isPublic.toString(),
"uploadedAt" to System.currentTimeMillis().toString()
)
)
}suspend fun uploadAttachment(fileName: String, fileData: ByteArray) {
DevengNetworkingModule.sendRequest<Unit, Unit>(
endpoint = "/attachments",
requestMethod = DevengHttpMethod.MULTIPART,
fileName = fileName,
fileContent = fileData,
fileFieldName = "attachment" // Custom field name instead of default "File"
)
}data class UploadResponse(val fileId: String, val downloadUrl: String)
suspend fun uploadAndGetUrl(fileName: String, fileData: ByteArray): UploadResponse {
return DevengNetworkingModule.sendRequest<Unit, UploadResponse>(
endpoint = "/files/upload",
requestMethod = DevengHttpMethod.MULTIPART,
fileName = fileName,
fileContent = fileData,
additionalFormFields = mapOf("source" to "mobile")
)
}suspend fun uploadFile(fileName: String, fileData: ByteArray): UploadResponse {
return DevengNetworkingModule.sendRequest(
endpoint = "/files/upload",
requestMethod = DevengHttpMethod.MULTIPART,
requestSerializer = null,
responseSerializer = UploadResponse.serializer(),
fileName = fileName,
fileContent = fileData
)
}Multipart File Upload Features:
val connection = DevengNetworkingModule.connectToWebSocket(
endpoint = "/chat",
onConnected = {
sendMessage("Hello Server!")
},
onMessageReceived = { message ->
println("Received: $message")
},
onError = { error ->
println("WebSocket Error: $error")
},
onClose = {
println("Connection closed")
}
)
// Send messages
connection.sendMessage("Hello World!")scope.launch {
connection.connectionState.collect { state ->
when (state) {
is ConnectionState.Connected -> println("β
Connected")
is ConnectionState.Connecting -> println("π Connecting...")
is ConnectionState.Disconnected -> println("β Disconnected")
is ConnectionState.Error -> println("β οΈ Error: ${state.error}")
}
}
}// Configure max connections (default: 5)
WebSocketConnection.setMaxConnections(3)
// Get connection stats
val activeConnections = WebSocketConnection.getActiveConnections()
val connectionCount = WebSocketConnection.getConnectionCount()
// Close specific connection
DevengNetworkingModule.closeWebSocketConnection("/chat")
// Close all connections
DevengNetworkingModule.closeAllWebSocketConnections()try {
val user = DevengNetworkingModule.sendRequest<Unit, User?>(
endpoint = "/users/123",
requestMethod = DevengHttpMethod.GET
)
} catch (e: DevengException) {
println("Error: ${e.message}") // Localized error message
}class CustomExceptionHandler : ExceptionHandler {
override var locale: Locale = Locale.EN
override fun handleHttpException(
errorMessage: String?,
status: HttpStatusCode
): DevengUiError {
// Custom HTTP error logic
return when (status.value) {
404 -> DevengUiError("Resource not found")
500 -> DevengUiError("Server error occurred")
else -> DevengUiError("Request failed: $errorMessage")
}
}
override fun handleNetworkException(cause: Throwable): DevengUiError {
// Custom network error logic
return DevengUiError("Network connection failed")
}
}// Update authentication token at runtime
DevengNetworkingModule.setToken("new-auth-token")Add to libs.versions.toml:
[versions]
devengNetworkingKmp = "latest"
[libraries]
deveng-networking-kmp = {
module = "global.deveng:networking-kmp",
version.ref = "devengNetworkingKmp"
}Then in build.gradle.kts:
dependencies {
implementation(libs.deveng.networking.kmp)
}The library is built on top of Ktor and provides:
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
We welcome contributions! Please:
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)For major changes, please open an issue first to discuss your proposal.
Created by Deveng Group - Building powerful, developer-friendly tools for the Kotlin ecosystem.
Made with β€οΈ by the Deveng Group team