Routes and HttpRoutes
Define endpoints functionally.
Routes and HttpRoutes is a free Scala for Backend Engineering & Functional Programming lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Scala for Backend Engineering & Functional Programming learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
What http4s Is
http4s is a purely functional HTTP library for Scala, built on cats-effect and fs2 streams. Requests and responses are immutable values, and effects are captured in a polymorphic effect type F[_] such as IO.
Instead of mutating a servlet, you describe an HTTP service as a function from a request to an effectful optional response.
The HttpRoutes Type
The core abstraction is HttpRoutes[F], an alias for Kleisli[OptionT[F, *], Request[F], Response[F]]. The OptionT models a route that may not match, returning no response.
You rarely write that signature by hand. Instead you build routes with the HttpRoutes.of constructor and a partial function.
import cats.effect.IO
import org.http4s._
import org.http4s.dsl.io._
val routes: HttpRoutes[IO] = HttpRoutes.of[IO] {
case GET -> Root / "hello" => Ok("hi")
}The http4s DSL
The DSL import org.http4s.dsl.io._ brings in pattern extractors like GET, Root, and the path separator /, plus response builders like Ok and NotFound.
A route is a PartialFunction[Request[F], F[Response[F]]]. Cases that do not match simply fall through.
import org.http4s.dsl.io._
HttpRoutes.of[IO] {
case GET -> Root => Ok("root")
case GET -> Root / "ping" => Ok("pong")
}Matching Paths
Path patterns read left to right. Root is the leading slash, and each / "segment" matches one literal path segment.
The arrow -> separates the HTTP method from the path. So GET -> Root / "users" matches GET /users.
HttpRoutes.of[IO] {
case GET -> Root / "users" => Ok("all users")
case GET -> Root / "users" / "me" => Ok("current user")
}Path Variables
A bare lowercase binder in a path captures that segment as a String. Here id binds whatever appears after /users/.
Captured segments are always strings; you parse them to richer types yourself or use extractor objects shown next.
HttpRoutes.of[IO] {
case GET -> Root / "users" / id =>
Ok(s"user $id")
}Typed Path Extractors
http4s ships extractors such as IntVar and LongVar that match only when the segment parses to that type. A non-numeric segment makes the case fall through to the next.
You can define custom extractors with an unapply for domain types like UUIDs.
HttpRoutes.of[IO] {
case GET -> Root / "users" / IntVar(id) =>
Ok(s"numeric user $id")
}Method Matching
The method extractor before -> can be any of GET, POST, PUT, DELETE, PATCH, and more. The same path with different methods becomes separate cases.
If a path matches but the method does not, http4s automatically responds 405 Method Not Allowed.
HttpRoutes.of[IO] {
case GET -> Root / "items" => Ok("list")
case POST -> Root / "items" => Created("made")
case DELETE -> Root / "items" / IntVar(i) => NoContent()
}Combining Routes
Because HttpRoutes[F] forms a SemigroupK, you compose multiple route groups with the <+> operator. The first group that produces a response wins.
This lets you split routes by feature into separate values and merge them in one place.
import cats.syntax.semigroupk._
val all = userRoutes <+> itemRoutes <+> healthRoutesRoutes to HttpApp
A server needs a total function, not a partial one. HttpRoutes[F] is converted to HttpApp[F] with .orNotFound, which supplies a 404 when no route matches.
HttpApp[F] is Kleisli[F, Request[F], Response[F]] — always returning a response.
import org.http4s.HttpApp
val app: HttpApp[IO] = routes.orNotFoundMiddleware Wrapping
Middleware are functions HttpRoutes[F] => HttpRoutes[F] (or over HttpApp). They wrap a service to add logging, CORS, gzip, or auth without touching route logic.
Built-in examples include Logger, CORS, and GZip from org.http4s.server.middleware.
import org.http4s.server.middleware.Logger
val logged = Logger.httpApp(logHeaders = true, logBody = false)(app)Query Parameters in Routes
Query parameters are matched with matcher objects extending QueryParamDecoderMatcher. They appear after a :? in the path pattern.
Use OptionalQueryParamDecoderMatcher for parameters that may be absent, yielding an Option.
object NameParam extends QueryParamDecoderMatcher[String]("name")
HttpRoutes.of[IO] {
case GET -> Root / "hi" :? NameParam(n) => Ok(s"hi $n")
}Quick Check
Test your understanding of route composition.
Recap
You built routes with HttpRoutes.of and the DSL, matched methods and paths, captured variables with IntVar and matchers, and composed groups with <+>.
Routes are partial; .orNotFound makes them total HttpApp, and middleware wraps services for cross-cutting concerns.
Frequently asked questions
Is the “Routes and HttpRoutes” lesson free?
Yes — the full text of “Routes and HttpRoutes” is free to read here on the web, and the Scala for Backend Engineering & Functional Programming course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Scala for Backend Engineering & Functional Programming course, upgrade to CoddyKit PRO.
What will I learn in “Routes and HttpRoutes”?
Define endpoints functionally. You practise Scala for Backend Engineering & Functional Programming with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Scala for Backend Engineering & Functional Programming?
No prior experience is required. Scala for Backend Engineering & Functional Programming on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Routes and HttpRoutes” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Scala for Backend Engineering & Functional Programming lesson?
Yes. Every Scala for Backend Engineering & Functional Programming lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Routes and HttpRoutes
- Requests and Responses
- JSON Endpoints
- Serving the Application