Custom DSL
The DSL module is designed to be extensible. You can create custom DSL implementations tailored to your project's needs by implementing the RoutingDslFactory interface.
Architecture
The DSL system consists of three main components:
- Factory — creates configuration and handlers
- Configuration — defines available route methods
- Scope — provides the handler context (what's available inside route lambdas)
Creating a Custom DSL
Step 1: Define a Custom Scope
The scope determines what methods are available inside route handlers:
open class CustomScope(override val ctx: Context) : DefaultContextScope, Context by ctx {
fun helloWorld(): String = "Hello, World!"
fun currentUser(): User = ctx.attribute("user")!!
fun endpoint(): HandlerEntry =
ctx.attribute<HandlerEntry>("javalin-handler-entry")!!
}Step 2: Create the Factory
Implement RoutingDslFactory to connect everything:
object CustomDsl : RoutingDslFactory<
CustomConfiguration,
DslRoute<CustomScope, Unit>,
CustomScope,
Unit
> {
class CustomConfiguration :
DefaultContextScopeConfiguration<DslRoute<CustomScope, Unit>, CustomScope, Unit>()
override fun createConfiguration(): CustomConfiguration =
CustomConfiguration()
override fun createHandler(route: DslRoute<CustomScope, Unit>): Handler =
Handler { ctx -> route.handler(CustomScope(ctx)) }
override fun createExceptionHandler(
handler: DslExceptionHandler<CustomScope, Exception, Unit>
): ExceptionHandler<Exception> =
ExceptionHandler { exception, ctx -> handler(CustomScope(ctx), exception) }
}Step 3: Use Your Custom DSL
Javalin.create { config ->
config.routes(DslRouting(CustomDsl)) {
get("/") {
// `helloWorld()` comes from CustomScope
result(helloWorld())
}
get("/me") {
json(currentUser())
}
}
}.start(8080)DefaultContextScope
The built-in DefaultContextScope interface provides a foundation for custom scopes:
interface DefaultContextScope {
val ctx: Context
}Implement it and delegate Context methods using Kotlin's by delegation:
class MyScope(override val ctx: Context) : DefaultContextScope, Context by ctx {
// your custom methods
}DefaultContextScopeConfiguration
The DefaultContextScopeConfiguration extends the base DSL configuration with type-safe path support:
open class DefaultContextScopeConfiguration<
ROUTE : DslRoute<CONTEXT, RESPONSE>,
CONTEXT : DefaultContextScope,
RESPONSE : Any
> : RoutingDslConfiguration<ROUTE, CONTEXT, RESPONSE>() {
inline fun <reified PATH : Any> get(
crossinline handler: CONTEXT.(PATH) -> RESPONSE
) { /* ... */ }
inline fun <reified PATH : Any> post(
crossinline handler: CONTEXT.(PATH) -> RESPONSE
) { /* ... */ }
// ... same for all HTTP methods
}This is what enables the get<PandaPath> { path -> ... } syntax.
Real-World Example
See the Reposilite project for a production example of custom DSL usage with Javalin routing extensions.