Request
The Request is an immutable case class that carries all information about an incoming HTTP request.
Properties
Section titled “Properties”case class Request( method: String, // HTTP method (GET, POST, etc.) url: String, // Full URL including query string path: String, // URL path only headers: Map[String, String], // Request headers (lowercase keys) params: Map[String, String], // Path parameters from route query: Map[String, Seq[String]], // Query string parameters (multi-valued) context: Context, // Type-safe extensible context for middleware data rawRequest: ServerRequest, // Underlying Node.js request object basePath: String, // Accumulated base path from subrouters finalizers: List[Finalizer], // Response transformers (LIFO) cookies: Map[String, String], // Parsed cookies)Path Parameters
Section titled “Path Parameters”Extract named segments from the route pattern:
server.get("/users/:id/posts/:postId", request => { val userId = request.params("id") val postId = request.params("postId") s"User $userId, Post $postId".asText})Query Parameters
Section titled “Query Parameters”Access query string values:
Query strings are multi-valued (repeated keys are preserved). Use queryParam for
the first value, or index query for all values of a key:
// GET /search?q=scala&page=2&tag=fp&tag=webserver.get("/search", request => { val q = request.queryParam("q").getOrElse("") // first value: Option[String] val page = request.queryParam("page").map(_.toInt).getOrElse(1) val tags = request.query.getOrElse("tag", Nil) // all values: Seq[String] s"Searching '$q' page $page tags ${tags.mkString(",")}".asText})Headers
Section titled “Headers”Headers are stored with lowercase keys for case-insensitive access:
val token = request.header("authorization") .filter(_.startsWith("Bearer ")) .map(_.substring(7))
val contentType = request.header("content-type")Body Parsing
Section titled “Body Parsing”Request body is read lazily as a stream. Four methods are available:
Raw Body
Section titled “Raw Body”request.body.flatMap { buffer: Buffer => // Process binary data buffer.asBinary}Text Body
Section titled “Text Body”Handles charset detection from the Content-Type header:
request.text.flatMap { text: String => s"Received: $text".asText}JSON Body
Section titled “JSON Body”Type-safe parsing with zio-json. Define your types with derives:
case class User(name: String, email: String) derives JsonDecoder
request.json[User].flatMap { case Some(user) => user.asJson(201) case None => "Invalid JSON".asText(400)}Form Data
Section titled “Form Data”URL-encoded form bodies (application/x-www-form-urlencoded):
Form bodies are multi-valued too (Map[String, Seq[String]]); formField returns
the first value of a field:
request.form.flatMap { formData: Map[String, Seq[String]] => val username = formData.get("username").flatMap(_.headOption).getOrElse("") val password = formData.get("password").flatMap(_.headOption).getOrElse("") processLogin(username, password)}Body Limits
Section titled “Body Limits”Configure maximum body size and read timeout:
// Per-server defaults via ServerConfigval server = Server(ServerConfig( maxBodySize = 50 * 1024 * 1024, // 50 MB (default) bodyTimeout = 30000, // 30 seconds (default)))For per-route limits, use BodyLimitMiddleware:
server.post("/upload", BodyLimitMiddleware(10 * 1024 * 1024), uploadHandler)Connection Info
Section titled “Connection Info”request.ip // Remote IP address (String)request.hostname // Host header value (Option[String])request.port // Remote port (Option[Int])request.protocol // "http" or "https" (String)request.secure // true if HTTPS (Boolean)request.httpVersion // HTTP version stringrequest.complete // Whether the request has been fully receivedrequest.aborted // Whether the client aborted the connectionCookies
Section titled “Cookies”// Access a cookie by namerequest.cookie("session") // Option[String]
// All cookiesrequest.cookies // Map[String, String]With CookieMiddleware enabled, additional methods are available:
request.getSignedCookie("auth") // Verified signed cookierequest.getJsonCookie[Settings]("prefs") // Parse JSON cookieContext
Section titled “Context”The context is a type-safe, immutable store (Context) that middleware use to pass
data to downstream handlers. Values are keyed by a TypedKey[A], so reads recover
the value’s type with no casting — define each key once and share it between the
writer and the reader:
// Define a key (typically a val in a companion object)val UserKey: TypedKey[User] = TypedKey("user")
// Middleware adds dataval withUser: Handler = request => { val user = lookupUser(request.params("id")) Future.successful(Continue( request.copy(context = request.context.updated(UserKey, user)) ))}
// Handler reads it — typed as Option[User], no cast neededval handler: Handler = request => request.context.get(UserKey) match { case Some(user) => user.asJson case None => failNotFound("User not found") }AuthMiddleware stores its Auth under AuthMiddleware.authKey:
request.context.get(AuthMiddleware.authKey) match { case Some(auth) => println(s"User: ${auth.user}, Roles: ${auth.roles}") case None => // Not authenticated}Adding Finalizers
Section titled “Adding Finalizers”Attach response transformers that run after a Complete result:
val modifiedRequest = request.addFinalizer { (req, response) => Future.successful(response.copy( headers = response.headers.add("X-Custom", "value") ))}Finalizers execute in LIFO order — the last one added runs first.