RESTful API Services is one of the most popular ways of building services on the web. Most programming languages have libraries to build and consume RESTful services. Similarly, Scala also provides many open-source REST libraries, such as Akka-HTTP, Play Framework, http4s, ZIO Http, and Finch. Additionally, other client libraries can be used to invoke REST requests. Apart from the previously mentioned REST frameworks, there are many other Java and Scala clients like sttp, async-http-client, OkHttp, etc. However, in many cases, invoking a simple REST service can require a lot of boilerplate code or configurations. In this tutorial, we’ll take a look at Requests-Scala to make REST requests easily.
Requests-Scala is a Scala port of the popular Python library Requests. It provides a straightforward API to make REST calls. Advantages of using Requests-Scala include no boilerplate code, simple setup, and intuitive and simpler API.
Let’s start by adding the Request-Scala using sbt-dependency as:
libraryDependencies += "com.lihaoyi" %% "requests" % "0.8.0"
4. HTTP Requests
Now, let’s look at how we can invoke the basic HTTP methods using Requests-Scala.
4.1. GET Requests
A simple GET request can be made using the get method:
val r: Response = requests.get("https://api.github.com/users/baeldung")
This will return a Response instance where we can use the text method to get the content:
val responseText = r.text assert(responseText.contains("http://www.baeldung.com")) assert(r.statusCode == 200) assert(r.contentType.exists(_.contains("application/json")))
The get method allows us to provide query parameters as a Map:
val r = requests.get("http://httpbin.org/get", params = Map("key1" -> "value1")) assert(r.statusCode == 200)
4.2. GET Request with Authentication
We can handle the authentication for the requests very easily. Let’s invoke a REST API with basic authentication:
val r = requests.get("https://postman-echo.com/basic-auth", auth = ("postman","password")) assert(r.statusCode == 200) assert(r.text().contains("true"))
In this case, we’re passing the username and password along with the request using the auth argument. Similarly, Requests-Scala also supports other authentication methods such as Proxy-Authorization, Bearer Token, Client Certificate, and so on.
4.3. POST Requests
A post method is available to us for making POST requests:
val r = requests.post("http://httpbin.org/post", data = Map("key"->"value")) assert(r.statusCode == 200)
The required POST form values can be passed as a Map using the data parameter. We can also pass any String value instead of the Map:
val r = requests.post("http://httpbin.org/post", data = "StringBody")
The authentication for the POST requests can also be applied in the same way as in the GET request.
4.4. Other HTTP Methods
We can make requests with HTTP methods like PUT, DELETE, HEAD and PATCH using the methods put(), delete(), head(), and patch() in the same way as we do for the GET request.
We can stream a response very easily with Requests-Scala. This is particularly useful if the response is too big and we want to improve the performance by loading only part of the data in memory at a time. Let’s look at how we can stream a large response from an API request and write it to a file. Just for simplicity, let’s use FileOutputStream to write the streaming content to the file:
val out = new FileOutputStream("events.json") val stream: geny.Readable = requests.get.stream("https://api.github.com/events") stream.writeBytesTo(out) out.close()
Another notable feature of Requests-Scala is the compression of the requests using Gzip and Deflate. While making the request, we need to set the compress param to the required compression mode. For Gzip compression, we can use:
requests.post( "https://httpbin.org/post", compress = requests.Compress.Gzip, data = "This is a gzipped post request" )
For Deflate compress, we only need to set the compress to requests.Compress.Deflate.
7. Multi-Part File Upload
To upload a file using the Multi-Part File Upload feature, we need to wrap the data param in MultiPart object as:
val r = requests.post( "http://httpbin.org/post", data = requests.MultiPart( requests.MultiItem("name", new java.io.File("file.txt"), "myFile.txt"), // you can upload strings, and file name is optional requests.MultiItem("hint", "This is file upload"), ) ) assert(r.text contains("multipart/form-data"))
Whenever we work with HTTP requests, we’ll need to handle the timeout for the request. The two major types of timeouts are:
- ConnectTimeout: The maximum time for a client to wait for establishing an HTTP connection
- ReadTimeout: The maximum time for a client to wait for receiving the response data
We can configure the above timeout values while making the requests by using the parameters connectTimeout and readTimeout for setting the desired timeout values. First, let’s look at how we can set readTimeout:
val r1 = requests.get("https://httpbin.org/delay/1", readTimeout = 1500)
If the request takes more than 1500ms to respond, then the client will terminate the connection with TimeoutException. In the same way, we can also set connectTimeout parameter. If it is taking more than the configured time to establish a connection, it will throw a TimeoutException:
val r2 = requests.get("https://httpbin.org/delay/3", connectTimeout = 1500)
9. Missing Features
There are some features missing from the Requests-Scala library. Requests-Scala doesn’t have any in-built support for asynchronous operations. However, we can easily make the requests asynchronous by wrapping in Future or using other similar libraries. Requests-Scala doesn’t have any in-built JSON parsing capabilities. However, we can use any JSON library to parse the JSON values. This makes the library lightweight, and users can use their favorite JSON libraries to handle JSON.
In this tutorial, we looked at Requests-Scala HTTP Client and how we can use it to make HTTP requests very easily. As always, the examples used in this tutorial are available over on GitHub.