Fork me on GitHub

Kweb is a library for building web applications in the Kotlin programming language.

Kweb is fairly unusual in that, while your code runs on the server, Kweb allows you to interact to the browser DOM directly as if it was local to the web server.

For example, here we create a <p> element and set its text:

doc.body.p().text("this is an example HTML paragraph")

Kweb has plugins for JavaScript libraries like JQuery and others. It’s also surprisingly easy to build your own plugins for other JavaScript libraries, or extend those Kweb already supports.

Features

  • Build websites in Kotlin
  • Makes the barrier between web-browser and web-server largely invisible to the programmer
  • Seamlessly integrates with powerful JavaScript libraries like JQuery, Semantic-UI, and others
  • Update your web browser instantly in response to code changes
  • Bind DOM elements in the browser directly to persistent state on the server and have them update automatically, through the observer and data mapper patterns, using the Shoebox persistent state store.
  • Easy to add to an existing project, Kweb is just a library, it doesn’t seek to tell you how your project should be organized

How does it work?

Kweb keeps all of the logic server-side, and uses efficient websockets to communicate to web browsers. We also take advantage of Kotlin’s powerful new coroutines mechanism to efficiently handle asynchronicity, largly invisibly to the programmer.

Can I see an example?

Here is the main code for a simple to-do list app, find the full app here:

fun main(args: Array<String>) {
    /** A simple yet flexible plugin mechanism */
    val plugins = listOf(semanticUIPlugin)

    /** Create a Kweb instance, and configure it to use the Semantic
     * UI framework. Build a simple to-do list app listening on
     * http://localhost:8091/
     * */
    Kweb(port = 8091, debug = true, plugins = plugins) {
        doc.body.new {

            /** Kweb allows you to modularize your code however suits your needs
                best.  Here I use an extension function defined elsewhere to
                draw some common outer page DOM elements */
            pageBorderAndTitle("Todo List") {

                /** A KVar is similar to an AtomicReference in the standard Java
                    Library, but which supports the observer pattern and `map`
                    semantics.  Here I set it to the current URL of the page.  This
                    will update automatically the page's URL changes, and can be
                    modified to update the page's URL, as you'll see below. */
                val url: KVar<URL> = doc.receiver.url(simpleUrlParser)

                /** s.content uses the semanticUIPlugin to use the excellent
                    Semantic UI framework, included as a plugin above, and implemented
                    as a convenient DSL within Kweb */
                div(s.content).new {

                    /** Note how url.path[0] is itself a KVar.  Changes to firstPathElement
                        will automatically propagate _bi-directionally_ with `url`.  This
                        comes in very handy later. */
                    val firstPathElement: KVar<String> = url.path[0]

                    /** Renders `firstPathElement`, but - and here's the fun part - will
                        automatically re-render if firstPathElement changes.  This is
                        a simple, elegant, and yet powerful routing mechanism. */
                    render(firstPathElement) { entityType ->
                        when (entityType) {
                            ROOT_PATH -> {
                                val newListId = createNewList()
                                url.path.value = listOf("lists", newListId)
                            }
                            "lists" -> {
                                /** Renders can be nested, which means that only this
                                    specific part of the page must be re-rendered if
                                    url.path[1] changes, which is very convenient
                                    for the developer in comparison to other frameworks,
                                    while being efficient under-the-hood. */
                                render(url.path[1]) { listId ->
                                    try {
                                        /** Here I use the same render mechanism to tie DOM
                                            state to persistent state stored in Shoebox,
                                            Kweb's simple but powerful key-value store with
                                            observer pattern support.  */
                                        renderList(toVar(State.lists, listId))
                                    } catch (e: NoSuchElementException) {
                                        throw NotFoundException("Can't find list with id $listId")
                                    }
                                }
                            }
                            else -> {
                                throw NotFoundException("Unrecognized entity type '$entityType', path: ${url.path.value}")
                            }
                        }
                    }
                }
            }
        }
    }
}

Next: Setting Up »»