Skip to main content

Installation

  1. Add InsForge dependencies to your project
build.gradle.kts:
repositories {
    mavenLocal() // For local development
    mavenCentral()
}

dependencies {
    implementation("dev.insforge:insforge-kotlin:0.1.5")
}
  1. Initialize InsForge SDK
import dev.insforge.createInsforgeClient
import dev.insforge.auth.Auth
import dev.insforge.database.Database
import dev.insforge.storage.Storage
import dev.insforge.functions.Functions
import dev.insforge.realtime.Realtime
import dev.insforge.ai.AI

val client = createInsforgeClient(
    baseUrl = "https://your-app.insforge.app",
    anonKey = "your-api-key"
) {
    install(Auth)
    install(Database)
    install(Storage)
    install(Functions)
    install(Realtime) {
        autoReconnect = true
        reconnectDelay = 5000
    }
    install(AI)
}
  1. Enable Logging (Optional)
For debugging, you can configure the SDK log level:
import dev.insforge.InsforgeLogLevel

val client = createInsforgeClient(
    baseUrl = "https://your-app.insforge.app",
    anonKey = "your-api-key"
) {
    // DEBUG: logs request method/URL and response status
    // VERBOSE: logs full headers and request/response bodies
    logLevel = InsforgeLogLevel.DEBUG

    install(Auth)
    install(Database)
    // ... other modules
}
Log LevelDescription
NONENo logging (default, recommended for production)
ERROROnly errors
WARNWarnings and errors
INFOInformational messages
DEBUGDebug info (request method, URL, response status)
VERBOSEFull details (headers, request/response bodies)
Use NONE or ERROR in production to avoid exposing sensitive data in logs.

Android Initialization

  1. Initialize InsForge SDK (With Local Storage and Browser Launcher for OAuth)
import android.content.Context
import android.content.Intent
import android.net.Uri
import dev.insforge.createInsforgeClient
import dev.insforge.ai.AI
import dev.insforge.auth.Auth
import dev.insforge.database.Database
import dev.insforge.functions.Functions
import dev.insforge.realtime.Realtime
import dev.insforge.storage.Storage
import dev.insforge.auth.BrowserLauncher
import dev.insforge.auth.SessionStorage

class InsforgeManager(private val context: Context) {
    
    val client = createInsforgeClient(
        baseUrl = "https://your-app.insforge.app",
        anonKey = "your-anon-key"
    ) {
        install(Auth) {
            // 1. config BrowserLauncher (for OAuth)
            browserLauncher = BrowserLauncher { url ->
                val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                context.startActivity(intent)
            }
            
            // 2. enable session persistence
            persistSession = true
            
            // 3. config SessionStorage (use SharedPreferences)
            sessionStorage = object : SessionStorage {
                private val prefs = context.getSharedPreferences(
                    "insforge_auth", 
                    Context.MODE_PRIVATE
                )
                
                override suspend fun save(key: String, value: String) {
                    prefs.edit().putString(key, value).apply()
                }
                
                override suspend fun get(key: String): String? {
                    return prefs.getString(key, null)
                }
                
                override suspend fun remove(key: String) {
                    prefs.edit().remove(key).apply()
                }
            }
        }
        // Install Database module
        install(Database)

        // Install Realtime module for real-time subscriptions
        install(Realtime) {
            debug = true
        }
        // Install other modules
        install(Storage)
        install(Functions)
        install(AI)
    }
}
  1. Use Jetpack DataStore for Session Storage (Optional)
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map

val Context.authDataStore: DataStore<Preferences> by preferencesDataStore(name = "insforge_auth")

class DataStoreSessionStorage(private val context: Context) : SessionStorage {
    
    override suspend fun save(key: String, value: String) {
        context.authDataStore.edit { prefs ->
            prefs[stringPreferencesKey(key)] = value
        }
    }
    
    override suspend fun get(key: String): String? {
        return context.authDataStore.data.map { prefs ->
            prefs[stringPreferencesKey(key)]
        }.first()
    }
    
    override suspend fun remove(key: String) {
        context.authDataStore.edit { prefs ->
            prefs.remove(stringPreferencesKey(key))
        }
    }
}

// Then use it in your InsForge client
install(Auth) {
    browserLauncher = ...
    persistSession = true
    sessionStorage = DataStoreSessionStorage(context)
}

invoke()

Invoke a serverless function by slug.

Parameters

  • slug (String) - Function slug identifier
  • body (Any?, optional) - Request body (will be JSON serialized)

Returns

T // Typed response (reified generic)

Examples

import kotlinx.serialization.Serializable
import kotlinx.serialization.SerialName

// Define response data class
@Serializable
data class HelloResponse(
    val message: String,
    val timestamp: String
)

// Invoke function with typed response
val response = client.functions.invoke<HelloResponse>(
    slug = "hello-world",
    body = mapOf("name" to "World")
)

println(response.message)  // "Hello, World!"

Example with Typed Request

@Serializable
data class GreetingRequest(
    val name: String,
    val greeting: String
)

@Serializable
data class GreetingResponse(
    val message: String,
    val timestamp: String
)

val response = client.functions.invoke<GreetingResponse>(
    slug = "hello-world",
    body = GreetingRequest(name = "Kotlin", greeting = "Hello")
)

Example: Fire-and-Forget

// Invoke without expecting a response
client.functions.invoke<Unit>(
    slug = "log-event",
    body = mapOf(
        "event" to "user_action",
        "action" to "button_click",
        "timestamp" to System.currentTimeMillis()
    )
)

invokeRaw()

Invoke a function and get the raw HTTP response.

Example

val response = client.functions.invokeRaw(
    slug = "generate-pdf",
    body = mapOf("documentId" to "doc-123")
)

// Access raw response
val bytes = response.readBytes()
val contentType = response.contentType()

Admin Functions

The following methods require admin/service role authentication.
These methods require the client to be initialized with a service role key instead of the anon key. Service role keys have elevated privileges and should only be used in secure server-side environments, never in client-side code.
// Initialize client with service role key for admin operations
val adminClient = createInsforgeClient(
    baseUrl = "https://your-app.insforge.app",
    anonKey = "your-service-role-key"  // Use service role key or api key, not anon key
) {
    install(Functions)
}

listFunctions()

List all functions.
val functions = client.functions.listFunctions()

functions.forEach { fn ->
    println("${fn.name} (${fn.slug}) - ${fn.status}")
}

getFunction()

Get specific function details.
val details = client.functions.getFunction("hello-world")

println("Name: ${details.name}")
println("Slug: ${details.slug}")
println("Status: ${details.status}")
println("Code: ${details.code}")

createFunction()

Currently, InsForge only supports JavaScript/TypeScript functions running in a Deno environment.
Create a new function.
val result = client.functions.createFunction(
    name = "Hello World",
    code = """
        export default async function(req) {
            const body = await req.json()
            return new Response(JSON.stringify({
                message: `Hello, ${'$'}{body.name}!`,
                timestamp: new Date().toISOString()
            }), {
                headers: { "Content-Type": "application/json" }
            })
        }
    """.trimIndent(),
    slug = "hello-world",  // Optional, auto-generated from name if not provided
    description = "A simple greeting function",
    status = "active"  // "draft" or "active"
)

println("Created function: ${result.slug}")

updateFunction()

Update an existing function.
val result = client.functions.updateFunction(
    slug = "hello-world",
    name = "Hello World v2",
    code = """
        export default async function(req) {
            const body = await req.json()
            return new Response(JSON.stringify({
                message: `Hello, ${'$'}{body.name}! Welcome to v2.`,
                version: 2
            }), {
                headers: { "Content-Type": "application/json" }
            })
        }
    """.trimIndent(),
    status = "active"
)

deleteFunction()

Delete a function.
client.functions.deleteFunction("old-function")

Error Handling

import dev.insforge.exceptions.InsforgeHttpException

try {
    val response = client.functions.invoke<MyResponse>("my-function")
    println("Success: $response")
} catch (e: InsforgeHttpException) {
    println("HTTP Error ${e.statusCode}: ${e.message}")
    println("Error code: ${e.error}")
    e.nextActions?.let { actions ->
        println("Suggested actions: $actions")
    }
} catch (e: Exception) {
    println("Unexpected error: ${e.message}")
}

Android Integration

ViewModel Example

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch

class FunctionViewModel : ViewModel() {

    private val _result = MutableStateFlow<Result<String>?>(null)
    val result: StateFlow<Result<String>?> = _result

    private val _isLoading = MutableStateFlow(false)
    val isLoading: StateFlow<Boolean> = _isLoading

    @Serializable
    data class ProcessRequest(val text: String)

    @Serializable
    data class ProcessResponse(val processed: String)

    fun processText(text: String) {
        viewModelScope.launch {
            _isLoading.value = true

            try {
                val response = insforge.functions.invoke<ProcessResponse>(
                    slug = "process-text",
                    body = ProcessRequest(text = text)
                )
                _result.value = Result.success(response.processed)
            } catch (e: Exception) {
                _result.value = Result.failure(e)
            }

            _isLoading.value = false
        }
    }
}

Jetpack Compose Example

@Composable
fun FunctionInvokeScreen(
    viewModel: FunctionViewModel = viewModel()
) {
    val result by viewModel.result.collectAsState()
    val isLoading by viewModel.isLoading.collectAsState()

    var inputText by remember { mutableStateOf("") }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp)
    ) {
        OutlinedTextField(
            value = inputText,
            onValueChange = { inputText = it },
            label = { Text("Enter text") },
            modifier = Modifier.fillMaxWidth()
        )

        Spacer(modifier = Modifier.height(16.dp))

        Button(
            onClick = { viewModel.processText(inputText) },
            enabled = inputText.isNotEmpty() && !isLoading,
            modifier = Modifier.fillMaxWidth()
        ) {
            if (isLoading) {
                CircularProgressIndicator(
                    modifier = Modifier.size(20.dp),
                    color = MaterialTheme.colorScheme.onPrimary
                )
            } else {
                Text("Process")
            }
        }

        Spacer(modifier = Modifier.height(16.dp))

        result?.let { res ->
            res.fold(
                onSuccess = { processed ->
                    Text(
                        text = "Result: $processed",
                        style = MaterialTheme.typography.bodyLarge
                    )
                },
                onFailure = { error ->
                    Text(
                        text = "Error: ${error.message}",
                        color = MaterialTheme.colorScheme.error
                    )
                }
            )
        }
    }
}

Complete Examples

AI-Powered Function

@Serializable
data class AIRequest(
    val prompt: String,
    val maxTokens: Int = 500
)

@Serializable
data class AIResponse(
    val text: String,
    val tokensUsed: Int
)

suspend fun generateWithAI(prompt: String): AIResponse {
    return insforge.functions.invoke(
        slug = "ai-generate",
        body = AIRequest(prompt = prompt)
    )
}

// Usage
val response = generateWithAI("Write a haiku about Kotlin")
println(response.text)
println("Tokens used: ${response.tokensUsed}")

Image Processing Function

@Serializable
data class ImageProcessRequest(
    val imageUrl: String,
    val filters: List<String>,
    val outputFormat: String = "jpeg"
)

@Serializable
data class ImageProcessResponse(
    val originalUrl: String,
    val processedUrl: String,
    val appliedFilters: List<String>
)

suspend fun processImage(imageUrl: String, filters: List<String>): ImageProcessResponse {
    return insforge.functions.invoke(
        slug = "process-image",
        body = ImageProcessRequest(
            imageUrl = imageUrl,
            filters = filters
        )
    )
}

// Usage
val result = processImage(
    imageUrl = "https://example.com/photo.jpg",
    filters = listOf("grayscale", "blur")
)
println("Processed image: ${result.processedUrl}")

Batch Processing

@Serializable
data class BatchRequest(
    val items: List<String>,
    val operation: String
)

@Serializable
data class BatchResult(
    val id: String,
    val success: Boolean,
    val message: String? = null
)

@Serializable
data class BatchResponse(
    val results: List<BatchResult>,
    val successCount: Int,
    val failureCount: Int
)

suspend fun processBatch(items: List<String>, operation: String): BatchResponse {
    return insforge.functions.invoke(
        slug = "batch-process",
        body = BatchRequest(items = items, operation = operation)
    )
}

// Usage
val response = processBatch(
    items = listOf("item-1", "item-2", "item-3"),
    operation = "validate"
)
println("Success: ${response.successCount}, Failed: ${response.failureCount}")

Models Reference

FunctionMetadata

@Serializable
data class FunctionMetadata(
    val id: String,
    val slug: String,
    val name: String,
    val description: String? = null,
    val status: String,  // "draft", "active", "error"
    @SerialName("created_at") val createdAt: String? = null,
    @SerialName("updated_at") val updatedAt: String? = null,
    @SerialName("deployed_at") val deployedAt: String? = null
)

FunctionDetails

@Serializable
data class FunctionDetails(
    val id: String,
    val slug: String,
    val name: String,
    val description: String? = null,
    val code: String,
    val status: String,  // "draft", "active", "error"
    @SerialName("created_at") val createdAt: String,
    @SerialName("updated_at") val updatedAt: String? = null,
    @SerialName("deployed_at") val deployedAt: String? = null
)