Installation
- Add InsForge dependencies to your project
Maven Central
GitHub Packages
build.gradle.kts:repositories {
mavenLocal() // For local development
mavenCentral()
}
dependencies {
implementation("dev.insforge:insforge-kotlin:0.1.5")
}
First, create a GitHub Personal Access Token:
- Go to GitHub → Settings → Developer settings → Personal access tokens → Tokens (classic)
- Select permission:
read:packages
Then configure your project using one of the following methods:Option A: Environment Variables
settings.gradle.kts (or build.gradle.kts):repositories {
mavenCentral()
maven {
url = uri("https://maven.pkg.github.com/InsForge/insforge-kotlin")
credentials {
username = System.getenv("GITHUB_USER") ?: ""
password = System.getenv("GITHUB_TOKEN") ?: ""
}
}
}
build.gradle.kts:dependencies {
implementation("dev.insforge:insforge-kotlin:0.1.3")
}
Set environment variables before building:export GITHUB_USER="your-github-username"
export GITHUB_TOKEN="your-personal-access-token"
Option B: gradle.properties (Local Development)
Add credentials to your global Gradle properties file:~/.gradle/gradle.properties:gpr.user=your-github-username
gpr.token=ghp_xxxxxxxxxxxx
settings.gradle.kts:repositories {
mavenCentral()
maven {
url = uri("https://maven.pkg.github.com/InsForge/insforge-kotlin")
credentials {
username = providers.gradleProperty("gpr.user").orNull ?: ""
password = providers.gradleProperty("gpr.token").orNull ?: ""
}
}
}
build.gradle.kts:dependencies {
implementation("dev.insforge:insforge-kotlin:0.1.1")
}
The ~/.gradle/gradle.properties file is stored outside your project, so credentials won’t be accidentally committed to version control.
- 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)
}
- 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 Level | Description |
|---|
NONE | No logging (default, recommended for production) |
ERROR | Only errors |
WARN | Warnings and errors |
INFO | Informational messages |
DEBUG | Debug info (request method, URL, response status) |
VERBOSE | Full details (headers, request/response bodies) |
Use NONE or ERROR in production to avoid exposing sensitive data in logs.
Android Initialization
- 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)
}
}
- 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)
}
from()
Get a bucket instance for file operations.
Example
val bucket = insforge.storage.from("images")
upload()
Upload a file with a specific path/key.
Parameters
path (String) - File path/key in the bucket
data (ByteArray) - File content as bytes
options (DSL block, optional) - Upload options
UploadOptions
| Option | Type | Description |
|---|
contentType | String? | MIME type (e.g., “image/jpeg”) |
upsert | Boolean | Overwrite if file exists (default: false) |
metadata | Map\<String, String\>? | Custom metadata |
Example
// Basic upload
val imageData = bitmap.toByteArray()
val result = insforge.storage
.from("images")
.upload("posts/post-123/cover.jpg", imageData)
Log.d("Storage", "Uploaded: ${result.url}")
// Upload with options
val result = insforge.storage
.from("images")
.upload("posts/post-123/cover.jpg", imageData) {
contentType = "image/jpeg"
upsert = true // Overwrite if exists
metadata = mapOf("userId" to "123")
}
// Using bracket syntax
val result = insforge.storage["images"]
.upload("avatars/user-123.jpg", imageData) {
contentType = "image/jpeg"
}
Upload from Android Uri
Developers can use the following function to upload files from an Android Uri:
// Android Uri Upload sample:
suspend fun uploadFromUri(
bucket: BucketApi,
uri: Uri,
context: Context,
path: String
): FileUploadResponse {
val data = context.contentResolver.openInputStream(uri)?.use {
it.readBytes()
} ?: throw IllegalArgumentException("Cannot read Uri")
val contentType = context.contentResolver.getType(uri)
return bucket.upload(path, data) {
this.contentType = contentType
}
}
uploadWithAutoKey()
Upload a file with auto-generated unique key.
Parameters
filename (String) - Original filename (used for extension detection)
data (ByteArray) - File content as bytes
options (DSL block, optional) - Upload options
Example
val imageData = bitmap.toByteArray()
val result = insforge.storage
.from("uploads")
.uploadWithAutoKey("photo.jpg", imageData) {
contentType = "image/jpeg"
}
Log.d("Storage", "Auto-generated key: ${result.key}")
Log.d("Storage", "URL: ${result.url}")
// Save to database
insforge.database
.from("posts")
.insertTyped(listOf(
Post(
imageUrl = result.url,
imageKey = result.key,
userId = userId
)
))
.returning()
.execute<Post>()
download()
Download a file as ByteArray.
Example
// Download file
val data = insforge.storage
.from("images")
.download(path = "posts/post-123/cover.jpg")
// Convert to Bitmap
val bitmap = BitmapFactory.decodeByteArray(data, 0, data.size)
imageView.setImageBitmap(bitmap)
delete()
Delete a file from storage.
Example
// Get the file key from database
val posts = insforge.database
.from("posts")
.select("image_key")
.eq("id", "post-123")
.execute<Post>() // Returns List<Post>
val post = posts.firstOrNull() ?: return
// Delete from storage
insforge.storage
.from("images")
.delete(post.imageKey)
// Clear database reference
insforge.database
.from("posts")
.update(buildJsonObject {
put("image_url", JsonNull)
put("image_key", JsonNull)
})
.eq("id", "post-123")
.execute<Post>()
createSignedUrl()
Create a signed URL for temporary access to a file.
Parameters
path (String) - File path in the bucket
expiresIn (Int) - Expiration time in seconds
Example
// Create a signed URL (expires in 1 hour)
val url = insforge.storage
.from("images")
.createSignedUrl("posts/post-123/cover.jpg", expiresIn = 3600)
Log.d("Storage", "Signed URL: $url")
getDownloadUrl()
Get a download URL for a file.
Example
val strategy = insforge.storage
.from("images")
.getDownloadUrl("posts/post-123/cover.jpg")
Log.d("Storage", "Download URL: ${strategy.url}")
list()
List files in a bucket with optional filtering.
BucketListFilter Options
| Option | Type | Description |
|---|
prefix | String? | Filter by path prefix |
limit | Int | Maximum results (default: 100) |
offset | Int | Pagination offset (default: 0) |
sortBy | String? | Sort field |
sortOrder | SortOrder | ASC or DESC (default: ASC) |
Example
// List all files in bucket
val files = insforge.storage
.from("images")
.list()
files.forEach { file ->
Log.d("Storage", "File: ${file.name}, Size: ${file.metadata?.size}")
}
// List with filters
val files = insforge.storage
.from("images")
.list {
prefix = "posts/"
limit = 50
offset = 0
sortBy = "created_at"
sortOrder = SortOrder.DESC
}
Bucket Management
listBuckets()
List all storage buckets.
val buckets = insforge.storage.listBuckets()
buckets.forEach { bucket ->
Log.d("Storage", "Bucket: ${bucket.id}, Public: ${bucket.isPublic}")
}
createBucket()
Create a new storage bucket.
val bucket = insforge.storage.createBucket("my-bucket") {
isPublic = true
}
Log.d("Storage", "Created bucket: ${bucket.id}")
updateBucket()
Update bucket settings.
insforge.storage.updateBucket("my-bucket") {
isPublic = false
}
deleteBucket()
Delete a storage bucket.
insforge.storage.deleteBucket("my-bucket")
Advanced Upload
getUploadStrategy()
Get upload strategy for large files or resumable uploads.
val strategy = insforge.storage
.from("videos")
.getUploadStrategy("videos/large-file.mp4", fileSize = 100_000_000)
// Use strategy.url for upload
Log.d("Storage", "Upload URL: ${strategy.url}")
confirmUpload()
Confirm a completed upload.
insforge.storage
.from("videos")
.confirmUpload("videos/large-file.mp4")
Jetpack Compose Integration
Image Picker with Upload
@Composable
fun ImageUploadScreen() {
var uploadedUrl by remember { mutableStateOf<String?>(null) }
var isUploading by remember { mutableStateOf(false) }
val context = LocalContext.current
val scope = rememberCoroutineScope()
// Helper function to read Uri as ByteArray
fun Uri.toByteArray(context: Context): ByteArray {
return context.contentResolver.openInputStream(this)?.use {
it.readBytes()
} ?: byteArrayOf()
}
val imagePickerLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.GetContent()
) { uri ->
uri?.let {
scope.launch {
isUploading = true
try {
// Read Uri to ByteArray
val imageData = it.toByteArray(context)
val result = insforge.storage
.from("photos")
.uploadWithAutoKey("photo.jpg", imageData) {
contentType = "image/jpeg"
}
uploadedUrl = result.url
} catch (e: Exception) {
Log.e("Upload", "Failed: ${e.message}")
} finally {
isUploading = false
}
}
}
}
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(onClick = { imagePickerLauncher.launch("image/*") }) {
Text("Select Photo")
}
Spacer(modifier = Modifier.height(16.dp))
if (isUploading) {
CircularProgressIndicator()
Text("Uploading...")
}
uploadedUrl?.let { url ->
AsyncImage(
model = url,
contentDescription = "Uploaded image",
modifier = Modifier
.fillMaxWidth()
.height(200.dp)
)
}
}
}
Async Image with InsForge
@Composable
fun InsForgeImage(
bucket: String,
path: String,
modifier: Modifier = Modifier
) {
var imageData by remember { mutableStateOf<ByteArray?>(null) }
var isLoading by remember { mutableStateOf(true) }
LaunchedEffect(bucket, path) {
try {
imageData = insforge.storage
.from(bucket)
.download(path)
} catch (e: Exception) {
Log.e("InsForgeImage", "Failed to load: ${e.message}")
} finally {
isLoading = false
}
}
if (isLoading) {
CircularProgressIndicator(modifier = modifier)
} else {
imageData?.let { data ->
val bitmap = BitmapFactory.decodeByteArray(data, 0, data.size)
Image(
bitmap = bitmap.asImageBitmap(),
contentDescription = null,
modifier = modifier
)
}
}
}