Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

35 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MolApi

Kotlin

MolApi is a Kotlin Multiplatform toolkit for registering API mocks and resolving incoming requests against them. The core idea is intentionally small:

AI Skills

The repository includes project-specific ai skills in ai-skills/. They provide implementation guidance for each MolApi library module

request -> registry.find(request) -> mock.matcher.matches(request) -> mock.response

The project currently targets Android and iOS for the common library modules, with additional Android-only adapters where the underlying stack is Android/JVM-only.

Installation

MolApi artifacts are published to Maven Central under the io.github.je-dog group. Add mavenCentral() to your repositories:

repositories {
    mavenCentral()
}

Then add only the modules your project needs. For a Kotlin Multiplatform project, keep common modules in commonMain and Android-only adapters in androidMain:

kotlin {
    sourceSets {
        commonMain.dependencies {
            implementation("io.github.je-dog:molapi-http:<latest_version>")
            implementation("io.github.je-dog:molapi-http-ktor:<latest_version>")
            implementation("io.github.je-dog:molapi-http-serialization:<latest_version>")

            // Optional: persistent mocks and Compose Multiplatform editor UI.
            implementation("io.github.je-dog:molapi-room:<latest_version>")
            implementation("io.github.je-dog:molapi-http-editor:<latest_version>")
        }

        androidMain.dependencies {
            implementation("io.github.je-dog:molapi-http-retrofit:<latest_version>")
            implementation("io.github.je-dog:molapi-http-gson:<latest_version>")
            implementation("io.github.je-dog:molapi-http-android-assets:<latest_version>")
        }
    }
}

For a regular Android Gradle module, add dependencies through the usual dependencies block:

dependencies {
    implementation("io.github.je-dog:molapi-http:<latest_version>")
    implementation("io.github.je-dog:molapi-http-retrofit:<latest_version>")
}

Modules

Module Maven Central Purpose Targets
:molapi-core Maven Central: molapi-core Generic request, matcher, response and registry contracts. Android, iOS
:molapi-http Maven Central: molapi-http HTTP request/response model, matchers, in-memory registry aliases and DSL. Android, iOS
:molapi-http-ktor Maven Central: molapi-http-ktor Ktor Client plugin that returns a mock response before a real request is sent. Android, iOS
:molapi-http-serialization Maven Central: molapi-http-serialization kotlinx.serialization helpers for JSON HTTP bodies. Android, iOS
:molapi-room Maven Central: molapi-room Room-backed persistent registry foundation. Android, iOS
:molapi-http-editor Maven Central: molapi-http-editor Compose Multiplatform screen for viewing, searching and editing HTTP mocks. Android, iOS
:molapi-http-android-assets Maven Central: molapi-http-android-assets Helps create JSON HTTP bodies from Android project assets. Android
:molapi-http-gson Maven Central: molapi-http-gson Gson helpers for JSON HTTP bodies. Android
:molapi-http-retrofit Maven Central: molapi-http-retrofit OkHttp/Retrofit interceptor integration. Android
:sample Not published Shared sample app code. Android, iOS
:sampleAndroidApp Not published Android sample application entry point. Android
SampleIosApp/ Not published iOS sample application entry point. iOS

HTTP Mocking

Create an HTTP registry and register mocks through the DSL. The main response overloads are shown below; get, post, put, patch, head, and delete have these response forms, while generic http also accepts an explicit HttpMethod:

import dag.khinkal.molapi.http.dsl.get
import dag.khinkal.molapi.http.dsl.http
import dag.khinkal.molapi.http.dsl.patch
import dag.khinkal.molapi.http.dsl.post
import dag.khinkal.molapi.http.dsl.put
import dag.khinkal.molapi.http.model.Headers
import dag.khinkal.molapi.http.model.HttpMethod
import dag.khinkal.molapi.http.model.HttpResponse
import dag.khinkal.molapi.http.model.HttpUrl
import dag.khinkal.molapi.http.model.JsonBody
import dag.khinkal.molapi.http.registry.HttpInMemoryApiMockRegistry

val registry = HttpInMemoryApiMockRegistry().apply {
    // A JSON string shortcut.
    get(
        path = "/todos",
        json = """[{"id":1,"title":"from molapi","completed":false}]""",
    )

    // A full response, including custom headers and status.
    post(
        url = HttpUrl(path = "/todos"),
        headers = Headers.jsonContent(),
        body = JsonBody("""{"title":"new"}"""),
        response = HttpResponse(
            body = JsonBody("""{"id":2,"title":"new","completed":false}"""),
            headers = Headers.jsonContent(),
            statusCode = 201,
        ),
    )

    // An arbitrary response body with its metadata.
    put(
        path = "/todos/2",
        response = JsonBody("""{"completed":true}"""),
        responseHeaders = Headers.jsonContent(),
        statusCode = 202,
    )

    // A response produced lazily from the request registration code.
    patch("/todos/2") {
        HttpResponse(body = JsonBody("""{"completed":false}"""))
    }

    // Without selected http method
    http(
        method = HttpMethod.DELETE,
        path = "/todos/2",
        response = HttpResponse(statusCode = 204),
    )
}

The HTTP DSL supports get, post, put, patch, head, delete, and generic http mocks. Each registered mock stores a matcher and a response:

  • URLs are structured as HttpUrl components: scheme, host, effective port, encoded path and order-independent query parameters.
  • Configured scheme and host use case-insensitive equality, port uses equality, and path keeps substring matching. Unspecified components are ignored.
  • Configured query parameter names and their value sets must match; additional request query names are allowed.
  • Method, headers and body matching use exact equality when those fields are provided.
  • If a matcher field is null, that field is ignored.
  • The registry returns the first matching mock.

Ktor Client

Install MolApiKtorPlugin into a Ktor HttpClient. If a registered mock matches the outgoing request, the plugin returns the mock response. If no mock matches, the request continues normally.

import dag.khinkal.molapi.http.ktor.plugin.MolApiKtorPlugin
import dag.khinkal.molapi.http.registry.HttpInMemoryApiMockRegistry
import io.ktor.client.HttpClient

val registry = HttpInMemoryApiMockRegistry()

val client = HttpClient {
    install(MolApiKtorPlugin) {
        this.registry = registry
    }
}

When using the editor integration, initialize MolApiEditor once at application startup and pass its shared registry into the plugin:

import dag.khinkal.molapi.http.editor.MolApiEditor
import dag.khinkal.molapi.http.ktor.plugin.MolApiKtorPlugin
import io.ktor.client.HttpClient

val client = HttpClient {
    install(MolApiKtorPlugin) {
        registry = MolApiEditor.registry
    }
}

sample/src/commonMain/kotlin/dag/khinkal/molapi/TodoApi.kt shows this pattern in the sample client configuration.

Retrofit and OkHttp

On Android, add MolApiRetrofitInterceptor through the OkHttp builder used by Retrofit:

import dag.khinkal.molapi.http.registry.HttpInMemoryApiMockRegistry
import dag.khinkal.molapi.http.retrofit.interceptor.addMolApiInterceptor
import okhttp3.OkHttpClient

val registry = HttpInMemoryApiMockRegistry()

val okHttpClient = OkHttpClient.Builder()
    .addMolApiInterceptor(registry)
    .build()

The interceptor follows the same fallback rule as the Ktor plugin: mock match first, real request otherwise.

When the editor is enabled, use the same shared registry:

import dag.khinkal.molapi.http.editor.MolApiEditor
import dag.khinkal.molapi.http.retrofit.interceptor.addMolApiInterceptor
import okhttp3.OkHttpClient

val okHttpClient = OkHttpClient.Builder()
    .addMolApiInterceptor(MolApiEditor.registry)
    .build()

JSON Helpers

Use molapi-http-serialization in multiplatform code:

import dag.khinkal.molapi.http.serializable.util.JsonHttpResponse
import kotlinx.serialization.Serializable

@Serializable
data class TodoDto(
    val id: Int,
    val title: String,
)

val response = JsonHttpResponse(
    serializableBody = TodoDto(id = 1, title = "from molapi"),
)

Use molapi-http-gson in Android code:

import dag.khinkal.molapi.http.gson.util.GsonHttpResponse
import dag.khinkal.molapi.http.model.Headers

val response = GsonHttpResponse(
    headers = Headers.jsonContent(),
    body = TodoDto(id = 1, title = "from molapi"),
)

Android Assets

Use molapi-http-android-assets when mock bodies are stored in Android project assets. The module only creates JsonBody values from asset files; request matching, response status and headers stay configured through the regular molapi-http API.

import android.content.Context
import android.content.res.AssetManager
import dag.khinkal.molapi.http.assets.AssetJsonBody
import dag.khinkal.molapi.http.dsl.post
import dag.khinkal.molapi.http.model.Headers
import dag.khinkal.molapi.http.model.HttpUrl
import dag.khinkal.molapi.http.model.JsonBody
import dag.khinkal.molapi.http.registry.HttpInMemoryApiMockRegistry

fun createRegistry(
    context: Context,
    assetManager: AssetManager,
): HttpInMemoryApiMockRegistry =
    HttpInMemoryApiMockRegistry().apply {
        post(
            url = HttpUrl(path = "/todos"),
            headers = Headers.jsonContent(),
            body = AssetJsonBody(context, "requests/create-todo.json"),
            responseHeaders = Headers.jsonContent(),
            statusCode = 201,
        ) {
            JsonBody.fromAssets(context, "responses/todo-created.json")
        }
        post(
            url = HttpUrl(path = "/todos"),
            headers = Headers.jsonContent(),
            body = AssetJsonBody(assetManager, "requests/create-todo.json"),
            responseHeaders = Headers.jsonContent(),
            statusCode = 201,
        ) {
            JsonBody.fromAssets(assetManager, "responses/todo-created.json")
        }
    }

AssetJsonBody(context, path) and JsonBody.fromAssets(context, path) read the asset text as UTF-8 by default. Pass a Charset explicitly if an asset uses another encoding.

Persistent Registry

:molapi-room provides RoomApiMockRegistry, which stores mocks in Room and restores them through an ApiMockParser. The parser owns serialization for matcher and response types, so a feature can decide which mock shapes are safe to persist.

Mocks use String ids. The default registry id generator is UuidApiMockIdGenerator, so new records get random UUID string ids unless a custom ApiMockIdGenerator is supplied.

For HTTP mocks, the editor module already ships the Room parser used by MolApiEditor.

Editor UI

:molapi-http-editor exposes a Compose Multiplatform MolApiHttpMockEditorScreen and a MolApiEditor facade. The facade owns one shared HttpApiMockRegistry so the editor UI and the network interceptor/plugin see the same mocks.

Initialize the editor once at application startup.

Android:

import android.app.Application
import dag.khinkal.molapi.http.editor.MolApiEditor
import dag.khinkal.molapi.http.editor.init

class App : Application() {
    override fun onCreate() {
        super.onCreate()
        MolApiEditor.init(this)
    }
}

iOS:

import dag.khinkal.molapi.http.editor.MolApiEditor
import dag.khinkal.molapi.http.editor.init

fun initializeMolApiEditor() {
    MolApiEditor.init()
}

Call the exported initializeMolApiEditor() from the Swift App.init() or another iOS application startup point before creating screens or HTTP clients.

import androidx.compose.runtime.Composable
import dag.khinkal.molapi.http.editor.MolApiHttpMockEditorScreen

@Composable
fun MockEditor() {
    MolApiHttpMockEditorScreen()
}

For tests or custom dependency injection, pass an explicit registry provider:

import dag.khinkal.molapi.http.editor.MolApiEditor
import dag.khinkal.molapi.http.ktor.plugin.MolApiKtorPlugin
import io.ktor.client.HttpClient

val customRegistry: HttpApiMockRegistry = CustomHttpApiMockRegistry() 

val client = HttpClient {
  install(MolApiKtorPlugin) {
    registry = customRegistry
  }
}

@Composable
fun TestScreen() {
  MolApiHttpMockEditorScreen(registry = { customRegistry })
}

The screen can list registered HTTP mocks, filter them, add new JSON mocks and clear or remove entries from the registry. Request and response JSON bodies can be typed inline or loaded through the platform document picker.

Running

Build the Android sample:

./gradlew :sampleAndroidApp:assembleDebug

Run focused library checks:

./gradlew :molapi-core:testAndroidHostTest :molapi-http:testAndroidHostTest
./gradlew :molapi-http-ktor:testAndroidHostTest
./gradlew :molapi-http-android-assets:testAndroidHostTest
./gradlew :sample:testAndroidHostTest

Run iOS simulator checks for common modules:

./gradlew :molapi-core:iosSimulatorArm64Test :molapi-http:iosSimulatorArm64Test
./gradlew :molapi-http-ktor:iosSimulatorArm64Test
./gradlew :sample:iosSimulatorArm64Test

The iOS app entry point is in SampleIosApp/; open that directory in Xcode to run the sample on an iOS simulator or device.

Development Notes

  • Public APIs use explicit Kotlin visibility.
  • Common modules avoid platform-only APIs in commonMain.
  • :molapi-http depends on :molapi-core as API because HTTP public types expose core contracts.
  • Transport integrations (:molapi-http-ktor and :molapi-http-retrofit) accept a registry from the caller; they do not depend on :molapi-http-editor.

License

Copyright 2026 MolApi Contributors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

About

Kotlin Multiplatform toolkit for registering API mocks and resolving incoming requests against them

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages