scalaintermediate

HTTP Client with sttp

Make HTTP requests with sttp: GET, POST, headers, JSON bodies, and async backends.

scala
import sttp.client3.*
import sttp.client3.circe.*
import io.circe.generic.auto.*
import io.circe.{Encoder, Decoder}

case class User(id: Int, name: String, email: String)
case class CreateUserRequest(name: String, email: String)
case class ApiResponse[T](data: T, status: String)

object HttpClient:
  // Synchronous backend
  val backend = HttpClientSyncBackend()

  def getUser(id: Int): Either[String, User] =
    val response = basicRequest
      .get(uri"https://api.example.com/users/$id")
      .header("Accept", "application/json")
      .header("Authorization", "Bearer token123")
      .response(asJson[User])
      .send(backend)

    response.body match
      case Right(user) => Right(user)
      case Left(error) => Left(s"Failed: ${error.getMessage}")

  def createUser(name: String, email: String): Either[String, User] =
    val body = CreateUserRequest(name, email)
    val response = basicRequest
      .post(uri"https://api.example.com/users")
      .header("Content-Type", "application/json")
      .body(body)
      .response(asJson[User])
      .send(backend)

    response.body.left.map(_.getMessage)

  def listUsers(page: Int, limit: Int): Either[String, List[User]] =
    val response = basicRequest
      .get(uri"https://api.example.com/users?page=$page&limit=$limit")
      .response(asJson[List[User]])
      .send(backend)

    response.body.left.map(_.getMessage)

  // Upload file
  def uploadFile(path: String): Either[String, String] =
    val response = basicRequest
      .post(uri"https://api.example.com/upload")
      .multipartBody(
        multipart("file", java.io.File(path)),
        multipart("description", "My file")
      )
      .response(asStringAlways)
      .send(backend)

    Right(response.body)

  // With timeout and retry
  def fetchWithRetry(url: String, retries: Int = 3): Either[String, String] =
    var lastError = ""
    for attempt <- 1 to retries do
      val response = basicRequest
        .get(uri"$url")
        .readTimeout(5.seconds)
        .response(asStringAlways)
        .send(backend)

      if response.code.isSuccess then
        return Right(response.body)
      else
        lastError = s"HTTP ${response.code}: ${response.body}"
        println(s"Attempt $attempt failed: $lastError")
    Left(lastError)

  def main(args: Array[String]): Unit =
    // Example usage (would need real API)
    println("HTTP Client ready")
    backend.close()

Use Cases

  • REST API integration
  • Microservice communication
  • File upload handling

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.