Instalación
- 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.6")
}
First, create a GitHub Personal Access Token:
- Go to GitHub → Settings → Developer settings → Personal access tokens → Tokens (classic)
- Select permission:
read:packages
Option A: Environment Variables
Option A: Environment Variables
settings.gradle.kts (or build.gradle.kts):build.gradle.kts:Set environment variables before building:
repositories {
mavenCentral()
maven {
url = uri("https://maven.pkg.github.com/InsForge/insforge-kotlin")
credentials {
username = System.getenv("GITHUB_USER") ?: ""
password = System.getenv("GITHUB_TOKEN") ?: ""
}
}
}
dependencies {
implementation("dev.insforge:insforge-kotlin:0.1.6")
}
export GITHUB_USER="your-github-username"
export GITHUB_TOKEN="your-personal-access-token"
Option B: gradle.properties (Local Development)
Option B: gradle.properties (Local Development)
Add credentials to your global Gradle properties file:~/.gradle/gradle.properties:settings.gradle.kts:build.gradle.kts:
gpr.user=your-github-username
gpr.token=ghp_xxxxxxxxxxxx
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 ?: ""
}
}
}
dependencies {
implementation("dev.insforge:insforge-kotlin:0.1.6")
}
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)
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
- Add Chrome Custom Tabs dependency to your
build.gradle.kts:
dependencies {
implementation("androidx.browser:browser:1.9.0")
}
- Initialize InsForge SDK (With Chrome Custom Tabs and Session Storage)
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.browser.customtabs.CustomTabsIntent
import dev.insforge.createInsforgeClient
import dev.insforge.ai.AI
import dev.insforge.auth.Auth
import dev.insforge.auth.BrowserLauncher
import dev.insforge.auth.ClientType
import dev.insforge.auth.SessionStorage
import dev.insforge.database.Database
import dev.insforge.functions.Functions
import dev.insforge.realtime.Realtime
import dev.insforge.storage.Storage
class InsforgeManager(private val context: Context) {
val client = createInsforgeClient(
baseUrl = "https://your-app.insforge.app",
anonKey = "your-anon-key"
) {
install(Auth) {
// 1. Chrome Custom Tabs - opens in-app, similar to iOS ASWebAuthenticationSession
browserLauncher = BrowserLauncher { url ->
val customTabsIntent = CustomTabsIntent.Builder()
.setShowTitle(true)
.build()
// Handle non-Activity context safely
if (context is Activity) {
customTabsIntent.launchUrl(context, Uri.parse(url))
} else {
customTabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
customTabsIntent.launchUrl(context, Uri.parse(url))
}
}
// 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()
}
}
// 4. set client type for mobile
clientType = ClientType.MOBILE
}
// 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)
}
insert()
Insertar nuevos registros en una tabla.Ejemplos
// Define your data class
@Serializable
data class Post(
val id: String? = null,
val title: String,
val content: String,
@SerialName("created_at")
val createdAt: String? = null
)
// Single insert (must wrap in list)
val post = Post(title = "Hello World", content = "My first post!")
val result = insforge.database
.from("posts")
.insertTyped(listOf(post))
.returning()
.execute<Post>() // Returns List<Post>
// Bulk insert
val posts = listOf(
Post(title = "First Post", content = "Hello everyone!"),
Post(title = "Second Post", content = "Another update.")
)
val result = insforge.database
.from("posts")
.insertTyped(posts)
.returning()
.execute<Post>() // Returns List<Post>
update()
Actualizar registros existentes en una tabla.Ejemplos
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
// Update by ID (using JsonPrimitive)
val result = insforge.database
.from("posts")
.update(mapOf("title" to JsonPrimitive("Updated Title")))
.eq("id", postId)
.returning()
.execute<Post>() // Returns List<Post>
// Update by ID (using buildJsonObject)
val result = insforge.database
.from("posts")
.update(buildJsonObject { put("title", "Updated Title") })
.eq("id", postId)
.returning()
.execute<Post>() // Returns List<Post>
// Update multiple
val result = insforge.database
.from("tasks")
.update(buildJsonObject { put("status", "completed") })
.`in`("id", listOf("task-1", "task-2"))
.returning()
.execute<Task>() // Returns List<Task>
delete()
Eliminar registros de una tabla.Ejemplos
// Delete by ID
insforge.database
.from("posts")
.delete()
.eq("id", postId)
.execute()
// Delete with filter
insforge.database
.from("sessions")
.delete()
.lt("expires_at", Date())
.execute()
select()
Consultar registros de una tabla.Ejemplos
// Get all posts
val posts = insforge.database
.from("posts")
.select()
.execute<Post>() // Returns List<Post>
// Specific columns
val posts = insforge.database
.from("posts")
.select("id, title, content")
.execute<Post>() // Returns List<Post>
// With relationships (typed)
@Serializable
data class PostWithComments(
val id: String,
val title: String,
val content: String,
val comments: List<Comment>
)
val posts = insforge.database
.from("posts")
.select("*, comments(id, content)")
.execute<PostWithComments>() // Returns List<PostWithComments>
executeRaw()
Ejecutar una consulta SELECT y devolver una matriz JSON sin procesar. Utiliza este método cuando necesites trabajar con datos dinámicos/sin tipo, como consultas con uniones que devuelven objetos anidados.Ejemplos
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
// Query with relationships - raw JSON access
val result = insforge.database
.from("tweets")
.select("id, content, profiles!tweets_user_id_fkey(username)")
.executeRaw() // Returns JsonArray
result.forEach { element ->
val obj = element.jsonObject
val id = obj["id"]?.jsonPrimitive?.content
val content = obj["content"]?.jsonPrimitive?.content
val profile = obj["profiles"]?.jsonObject
val username = profile?.get("username")?.jsonPrimitive?.content
println("Tweet by $username: $content")
}
// Dynamic queries where structure is unknown
val rawData = insforge.database
.from("dynamic_table")
.select()
.executeRaw()
// Process raw JSON as needed
rawData.forEach { element ->
val obj = element.jsonObject
obj.keys.forEach { key ->
println("$key: ${obj[key]}")
}
}
rpc()
Llamar a funciones almacenadas de PostgreSQL (RPC - Llamada a procedimiento remoto). Este método te permite invocar directamente funciones SQL definidas en tu base de datos.Firma
// Typed RPC call - deserializes response to specified type
suspend inline fun <reified T> rpc(
functionName: String,
args: Map<String, Any?>? = null
): T
// Raw RPC call - returns JsonElement for dynamic processing
suspend fun rpcRaw(
functionName: String,
args: Map<String, Any?>? = null
): JsonElement
Parámetros
functionName(String) - Nombre de la función PostgreSQL a llamarargs(Map\<String, Any?\>?, optional) - Argumentos a pasar a la función
Detalles de implementación
- Sin argumentos: Utiliza solicitud GET a
/api/database/rpc/{functionName} - Con argumentos: Utiliza solicitud POST con cuerpo JSON a
/api/database/rpc/{functionName}
Ejemplos
// Define response data classes
@Serializable
data class UserStats(
val totalPosts: Int,
val totalLikes: Int,
val joinedAt: String
)
@Serializable
data class User(
val id: String,
val name: String,
val email: String
)
// Call function with parameters
val stats = insforge.database.rpc<UserStats>(
"get_user_stats",
mapOf("user_id" to 123)
)
println("Total posts: ${stats.totalPosts}")
// Call function without parameters
val users = insforge.database.rpc<List<User>>("get_all_active_users")
users.forEach { user ->
println("User: ${user.name}")
}
// Call function returning a single value
val count = insforge.database.rpc<Int>("count_active_posts")
println("Active posts: $count")
// Call function with multiple parameters
val result = insforge.database.rpc<List<Post>>(
"search_posts",
mapOf(
"search_term" to "kotlin",
"limit" to 10,
"offset" to 0
)
)
Ejemplos de rpcRaw()
UtilizarpcRaw() cuando el tipo de retorno sea dinámico o desconocido en tiempo de compilación.
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
// Get raw JSON response
val result = insforge.database.rpcRaw(
"some_dynamic_function",
mapOf("param" to "value")
)
// Process based on actual response structure
when (result) {
is JsonArray -> {
result.forEach { element ->
val obj = element.jsonObject
println("Item: ${obj["name"]?.jsonPrimitive?.content}")
}
}
is JsonObject -> {
println("Single result: ${result["data"]}")
}
is JsonPrimitive -> {
println("Value: ${result.content}")
}
}
// Handle complex nested structures
val complexResult = insforge.database.rpcRaw("get_dashboard_data")
val dashboard = complexResult.jsonObject
val userCount = dashboard["user_count"]?.jsonPrimitive?.int
val recentPosts = dashboard["recent_posts"]?.jsonArray
Filtros
| Filtro | Descripción | Ejemplo |
|---|---|---|
.eq(column, value) | Igual a | .eq("status", "active") |
.neq(column, value) | No igual a | .neq("status", "banned") |
.gt(column, value) | Mayor que | .gt("age", 18) |
.gte(column, value) | Mayor que o igual a | .gte("price", 100) |
.lt(column, value) | Menor que | .lt("stock", 10) |
.lte(column, value) | Menor que o igual a | .lte("priority", 3) |
.like(column, pattern) | Patrón sensible a mayúsculas | .like("name", "%Widget%") |
.ilike(column, pattern) | Patrón insensible a mayúsculas | .ilike("email", "%@gmail.com") |
.in(column, list) | Valor en lista | .in("status", listOf("pending", "active")) |
.isNull(column) | Es nulo | .isNull("deleted_at") |
// Chain multiple filters
val products = insforge.database
.from("products")
.select()
.eq("category", "electronics")
.gte("price", 50)
.lte("price", 500)
.execute<Product>() // Returns List<Product>
Modificadores
| Modificador | Descripción | Ejemplo |
|---|---|---|
.order(column, ascending) | Ordenar resultados | .order("created_at", ascending = false) |
.limit(count) | Limitar filas | .limit(10) |
.range(from, to) | Paginación | .range(0, 9) |
// Pagination with sorting
val posts = insforge.database
.from("posts")
.select()
.order("created_at", ascending = false)
.range(0, 9)
.limit(10)
.execute<Post>() // Returns List<Post>