0Pricing
Scala for Backend Engineering & Functional Programming · Lesson

Requests and Responses

Read input and return output.

Requests and Responses is a free Scala for Backend Engineering & Functional Programming lesson on CoddyKit — lesson 2 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.

Request as a Value

In http4s a Request[F] is an immutable value carrying the method, URI, headers, HTTP version, and a streaming body. You inspect it with pure accessors and never mutate it in place.

To change a request you produce a copy via withHeaders, withUri, or similar combinators.

val r: Request[IO] = Request[IO](
  method = Method.GET,
  uri = uri"/health"
)

The Streaming Body

The body of a request or response is an fs2.Stream[F, Byte]. It is consumed lazily and effectfully, so large payloads never have to sit fully in memory.

You usually do not touch raw bytes; an EntityDecoder handles decoding for you.

// raw access (rarely needed)
val bytes: fs2.Stream[IO, Byte] = request.body

Decoding the Body

An EntityDecoder[F, A] knows how to turn the body stream into an A. Call req.as[A] to decode, which returns F[A] and fails the effect on a malformed body.

A built-in decoder exists for String; richer ones come from JSON modules.

HttpRoutes.of[IO] {
  case req @ POST -> Root / "echo" =>
    req.as[String].flatMap(body => Ok(body))
}

Binding the Request

To access the request object inside a case, bind it with @. Here req names the whole request while the pattern still matches method and path.

Without the binder you only have the destructured parts and cannot read the body or headers.

HttpRoutes.of[IO] {
  case req @ PUT -> Root / "name" =>
    req.as[String].flatMap(n => Ok(s"set $n"))
}

Reading Headers

Headers live in request.headers. Use headers.get[H] with a typed header model, or headers.get(CIString("X-Custom")) for arbitrary names.

Typed headers like Authorization and Content-Type parse and validate their values for you.

import org.http4s.headers.Authorization

val auth: Option[Authorization] =
  request.headers.get[Authorization]

Building Responses

The DSL response builders such as Ok, Created, and NotFound are functions returning F[Response[F]]. Passing an argument sets the body via an EntityEncoder.

Each builder corresponds to a status code, so Created(...) yields 201.

Ok("all good")          // 200
Created("made it")      // 201
NotFound("missing")     // 404

Status Codes Directly

For a status without a convenience builder, or to customize, construct a Response[F] with an explicit Status and chain withEntity.

This gives full control over status, headers, and body in one expression.

import org.http4s.{Response, Status}

Response[IO](Status.Accepted)
  .withEntity("queued")
  .pure[IO]

Encoding Bodies

An EntityEncoder[F, A] serializes a value into the response body and sets the Content-Type. String and byte encoders are built in; JSON encoders come from circe.

When you pass a value to Ok(value), http4s looks up the encoder implicitly.

// String encoder is implicit; sets text/plain
val resp: IO[Response[IO]] = Ok("plain text body")

Setting Response Headers

Add headers by passing them as extra arguments to a builder, or chain .map(_.putHeaders(...)) on the response effect.

Typed header models ensure correctly formatted values, for example a Location header on a 201 Created.

import org.http4s.headers.Location

Created("ok").map(_.putHeaders(
  Location(uri"/items/42")
))

Handling Decode Failures

req.as[A] fails the F effect when the body cannot be decoded. Use req.attemptAs[A].value to get an F[Either[DecodeFailure, A]] and respond with 400 on the left.

This keeps malformed-input handling explicit and total.

req.attemptAs[String].value.flatMap {
  case Right(b) => Ok(b)
  case Left(_)  => BadRequest("bad body")
}

Cookies and Redirects

Set cookies with response.addCookie(ResponseCookie(name, value)). For redirects, use a 3xx builder and a Location header.

SeeOther(Location(uri)) performs a 303, the idiomatic post-redirect-get response.

import org.http4s.headers.Location

SeeOther(Location(uri"/login"))
  .map(_.addCookie("sid", "abc123"))

Quick Check

Think about safe body decoding.

Recap

Requests and responses are immutable values with streaming fs2 bodies. You decode with as/attemptAs via EntityDecoder and encode with EntityEncoder.

Builders set status codes, typed headers stay safe, and cookies and redirects are just response transformations.

Frequently asked questions

Is the “Requests and Responses” lesson free?

Yes — the full text of “Requests and Responses” 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 “Requests and Responses”?

Read input and return output. 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 2 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Requests and Responses” 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

  1. Routes and HttpRoutes
  2. Requests and Responses
  3. JSON Endpoints
  4. Serving the Application
← Back to Scala for Backend Engineering & Functional Programming