A lightweight REST framework written in pure Kotlin with zero external dependencies. Perfect for building microservices, REST APIs, and simple web servers.
β¨ Pure Kotlin - No external framework dependencies
π Lightweight - Minimal footprint (~316 LOC)
π HTTPS Support - Built-in SSL/TLS support
π£οΈ Flexible Routing - Path-based and regex routing
π― Filters - Before/after request filters
β‘ Simple API - Easy to learn and use
π Static Files - Built-in static file serving
π Exception Handling - Custom exception handlers
Add Kottpd to your project using Maven:
<dependency>
<groupId>com.github.gimlet2</groupId>
<artifactId>kottpd</artifactId>
<version>0.2.2</version>
</dependency>Or Gradle:
implementation("com.github.gimlet2:kottpd:0.2.2")import com.github.gimlet2.kottpd.Server
fun main() {
val server = Server() // Default port is 9000
server.get("/hello") { _, _ ->
"Hello, World!"
}
server.start()
println("Server running at http://localhost:9000")
}Visit http://localhost:9000/hello to see your server in action!
val server = Server(port = 8080)
// GET request
server.get("/users") { req, res ->
res.send("[{\"id\": 1, \"name\": \"John\"}]")
}
// POST request
server.post("/users") { req, res ->
val userData = req.content
res.send("User created: $userData", Status.Created)
}
// PUT request
server.put("/users/:id") { req, res ->
res.send("User ${req.url} updated")
}
// DELETE request
server.delete("/users/:id") { req, res ->
res.send("User deleted", Status.NoContent)
}
server.start()server.get("/api/v.*/users") { req, res ->
res.send("Matches /api/v1/users, /api/v2/users, etc.")
}
server.get("/files/.*\\.pdf") { req, res ->
res.send("PDF file requested")
}// Global before filter (runs before all requests)
server.before { req, res ->
println("Incoming request: ${req.method} ${req.url}")
res.send("Log: Request received\n")
}
// Path-specific before filter
server.before("/api/.*") { req, res ->
// Authentication check
val token = req.headers["Authorization"]
if (token == null) {
res.send("Unauthorized", Status.Unauthorized)
}
}
// After filter
server.after("/.*") { req, res ->
res.send("\n--- Request completed ---")
}server.get("/error") { req, res ->
throw IllegalStateException("Something went wrong!")
}
server.exception(IllegalStateException::class) { req, res ->
res.send("Error handled gracefully", Status.InternalServerError)
}// Serve static files from resources/public
server.staticFiles("/public")
// Now files are accessible:
// resources/public/index.html -> http://localhost:9000/index.html
// resources/public/css/style.css -> http://localhost:9000/css/style.cssserver.start(
port = 9443,
secure = true,
keyStoreFile = "./keystore.jks",
password = "keystorePassword"
)server.get("/headers") { req, res ->
val userAgent = req.headers["User-Agent"]
val contentType = req.headers["Content-Type"]
res.send("User-Agent: $userAgent")
}server.post("/data") { req, res ->
val body = req.content
val contentLength = req.headers["Content-Length"]
res.send("Received ${contentLength} bytes: $body", Status.Created)
}fun main() {
Server(9000).apply {
// Static files
staticFiles("/public")
// Routes
get("/") { _, _ -> "Welcome to Kottpd!" }
get("/hello") { _, res -> res.send("Hello") }
get("/user/.*") { req, res -> res.send("User path: ${req.url}") }
// POST with body
post("/data") { req, res ->
res.send(req.content, Status.Created)
}
// Filters
before("/hello") { _, res -> res.send("before\n") }
after("/hello") { _, res -> res.send("\nafter") }
// Global filter
before { _, res -> res.send("[LOG] ") }
// Exception handling
get("/error") { _, _ -> throw IllegalStateException("Test error") }
exception(IllegalStateException::class) { _, _ -> "Error handled" }
start()
}.also {
println("Server started on http://localhost:9000")
println("Try: http://localhost:9000/hello")
}
}Kottpd uses a simple architecture:
- Server - Main class that handles routing and server lifecycle
- ClientThread - Processes individual HTTP requests in separate threads
- HttpRequest - Represents incoming HTTP requests
- HttpResponse - Represents outgoing HTTP responses
- Status - HTTP status codes
- HttpMethod - Supported HTTP methods
val server = Server(port = 8080)
// or via system property
// -Dserver.port=8080The server uses a cached thread pool by default, which creates new threads as needed and reuses previously constructed threads when available.
- π Development Roadmap - Future plans and features
- π€ Contributing Guide - How to contribute
- π Security Best Practices - Security guidelines
| Feature | Kottpd | Ktor | Javalin | Spring Boot |
|---|---|---|---|---|
| Size | Tiny | Medium | Small | Large |
| Dependencies | None | Many | Few | Many |
| Learning Curve | Easy | Medium | Easy | Steep |
| Performance | Good | Excellent | Good | Good |
| Best For | Simple APIs | Production apps | REST APIs | Enterprise |
- No built-in JSON serialization (bring your own library)
- Basic error handling
- Limited documentation
- No built-in CORS support
- Early development stage (v0.2.x)
See ROADMAP.md for planned improvements.
We welcome contributions! Please see CONTRIBUTING.md for details.
This project is licensed under the MIT License - see the LICENSE file for details.
Andrei Chernyshev - @gimlet2
- π Report a bug
- π‘ Request a feature
- β Star this repository if you find it useful!
Made with β€οΈ using Kotlin