// WHY HTTP4K EXISTS

It's not another HTTP library. It's a different way of thinking.

Most frameworks bury HTTP under annotations, reflection and a container. http4k throws all of that out and starts from one radical idea. Every one of the 200+ modules is built on it, and nothing sneaks in behind your back.

// THE ONE IDEA

The server is the client.

This is the part other HTTP libraries miss. The same single type - (Request) -> Response - models both sides of the wire. Your server is an HttpHandler. Your HTTP client is also an HttpHandler. So a real remote call and an in-memory fake are interchangeable - a superpower that falls straight out of the simplicity.

Server.kt an HttpHandler
// a server takes a request, returns a response
val server: HttpHandler = { req -> Response(OK).body("pong") }
server(Request(GET, "/ping"))
Client.kt ...also an HttpHandler
// a client has the EXACT same type
val client: HttpHandler = JavaHttpClient()
// so calls are identical - swap one for
// the other with zero ceremony
client(Request(GET, "http://.../ping"))
One type, both sides. Because a client is just an HttpHandler, you can hand your code a real one in production and an in-memory fake in tests - the same Filters (logging, metrics, caching, retries) compose onto either. Symmetry is the whole trick.
// WHERE IT CAME FROM

A 2013 paper, refined for HTTP.

The idea traces to the "Your Server as a Function" paper from Twitter - the observation that every system boundary can be modelled as a function from request to response. We took that generic concept and refined it specifically for HTTP.

Then we extended the same model outward - WebSockets, Server-Sent Events, typesafe message handling via Lenses, function-based API clients - proving most things compose. The core idea never changed: simplicity.

CoreTypes.kt
// server AND client...
typealias HttpHandler = (Request) -> Response

// ...and everything that wraps either.
typealias Filter = (HttpHandler) -> HttpHandler

// that's it. that's the toolkit.
// WHAT WE WANTED

Nine boxes we refused to leave unticked.

http4k was built from scratch to fully leverage Kotlin, against a non-negotiable set of principles. Every one of these is still true today.

01

Functional & immutable

Built on simple functional concepts, embracing immutability throughout.

02

Symmetric API

The same "Server as a Function" model for both server and client.

03

Absolutely no magic

No reflection. No annotations. No bytecode surprises. What you read is what runs.

04

Lightweight

Minimal dependencies - beyond the Kotlin StdLib, http4k-core has exactly zero.

05

Test-driven

Testable outside any HTTP container, with no custom infrastructure needed.

06

Starts & stops fast

Ultra-quick start/stop times - ideal for serverless and rapid test cycles.

07

Typesafe messages

Typesafe HTTP message construction and deconstruction via Lenses.

08

No boilerplate

Contract breaches are dealt with automatically, removing reams of boilerplate.

09

Specs from code

Typed routes generate the OpenAPI spec and JSON Schema, so docs never drift from what runs.

// CREDIT WHERE IT'S DUE

Good artists borrow. We just stole the lot.

We're not precious about it. http4k distilled decades of using server-side libraries, and we pilfered the best parts shamelessly - then made them play together, the Kotlin way. Credit where it's due.

ROUTING

The shape of the routing module - and what convinced us that simple was possible.

MODEL

"Server as a Function" and the filter model - the underlying concept for everything.

OUR V1

http4k's Scala-based ancestor. Everyone agreed it was too complex - so we threw it out and started again in Kotlin.

// THE PAYOFF

Testability isn't a feature we added. It's what falls out.

When both your server and your clients are the same kind of function, the hard things get easy - and some things other HTTP libraries simply can't do become trivial.

Test without a container

Wire entire suites of services together without HTTP, or spin them up in containers with a single line. No custom infrastructure required.

Fake any contract

Because a client is just a function, a Fake server for any HTTP contract is trivial. With CDC suites, you can end-to-end test microservices outside-in, GOOS-style.

Model the nasty scenarios

"What if this dependency takes >5s, every time?" Easy to model - and impossible to answer if you fake things inside the HTTP boundary.

Convinced, or want to see it for yourself?

Dig into the core concepts, or skip straight to your first running server.

scarf