ZIO HTTP: A collection of Route examples

I try not to do this too often, but here’s a currently-incomplete list of ways to define ZIO HTTP routes. I start with some of the simplest examples, and then make them more complicated as I go along:

//> using scala "3"
//> using dep "dev.zio::zio::2.1.13"
//> using dep "dev.zio::zio-http::3.0.1"

import zio.*
import zio.http.*

/**
 * See more examples here:
 *     - https://zio.dev/zio-http/reference/handler
 */
object RoutingExamples extends ZIOAppDefault:

    // basic route definitions
    val routes1 = Routes(
        Method.GET / "hello" -> Handler.text("Hello World!"),
        Method.GET / "html1" -> Handler.html("<p>hi 1</p>"),
        Method.GET / "html2" -> handler(Response.html("<p>hi 2</p>")),
        Method.GET / "json"  -> handler(Response.json("""{"message": "Hello"}""")),
        // from https://zio.dev/zio-http/reference/handler:
        Method.GET / "uuid" ->
            handler(Random.nextUUID.map(u => Response.text(u.toString)))
    )

    // routes with different HTTP methods
    val routes2 = Routes(
        Method.GET    / "api" -> Handler.text("GET request"),
        Method.POST   / "api" -> Handler.text("POST request"),
        Method.PUT    / "api" -> Handler.text("PUT request"),
        Method.DELETE / "api" -> Handler.text("DELETE request"),
        Method.PATCH  / "api" -> Handler.text("PATCH request")
    )

    // routes with path parameters
    val routes3 = Routes(
        Method.GET / "users" / string("id") -> handler { (id: String, _: Request) =>
            Response.text(s"User ID: $id")
        },
        Method.GET / "users" / string("userId") / "posts" / string("postId") -> 
            handler { (userId: String, postId: String, _: Request) =>
                Response.text(s"User $userId, Post $postId")
            }
    )

    // routes with query parameters
    val routes4 = Routes(
        Method.GET / "search" -> handler { (req: Request) =>
            val query = req.url.queryParams.getAll("q").headOption
            Response.text(s"Search query: ${query.getOrElse("none")}")
        },
        Method.GET / "filter" -> handler { (req: Request) =>
            val sort  = req.url.queryParams.getAll("sort").headOption.getOrElse("asc")
            val limit = req.url.queryParams.getAll("limit").headOption.map(_.toInt).getOrElse(10)
            Response.text(s"Sort: $sort, Limit: $limit")
        }
    )

    // routes with request body handling
    val routes5 = Routes(
        // from https://zio.dev/zio-http/reference/routing/routes
        Method.POST / "echo1" ->
            handler { (req: Request) =>
                req.body.asString.map(Response.text(_))
            }.sandbox
        ,
        // echoes the request body
        // from https://zio.dev/zio-http/reference/handler
        Method.POST / "echo2" ->
            handler { (req: Request) =>
                req.body.asString(Charsets.Utf8).map(Response.text(_)).orDie
            }
        ,
        Method.POST / "json-body" -> handler { (req: Request) =>
            req.body.asString.orDie.map { body =>
                Response.json(s"""{"received": $body}""")
            }
        },
        Method.POST / "form" -> handler { (req: Request) =>
            (for
                form <- req.body.asURLEncodedForm
                name  = form.get("name").getOrElse("anonymous")
            yield 
                Response.text(s"Hello, $name!")).orDie
        }
    )

    // routes with different response types
    val routes6 = Routes(
        Method.GET / "status" -> Handler.status(Status.Created),
        Method.GET / "with-headers" -> handler(
            Response.json("""{"status": "ok"}""")
                    .addHeader(Header.Custom("X-Custom-Header", "value")))
        // Method.GET / "binary" ->
        //     Handler.fromStreamChunked(Chunk.fromArray("Hello".getBytes()))
    )

    // combining multiple route groups
    val allRoutes = routes1 ++ routes2 ++ routes3 ++ routes4 ++ routes5 ++ routes6

    // running the server
    override def run = 
        Server.serve(allRoutes)
              .provide(Server.default)

As I mentioned, this is a work-in-progress, and I’ll be updating this page over the next few days as I verify all the different possible ZIO HTTP examples.