HTTP Service for Weather Info Using Scala's Typelevel Stack
Sample HTTP Service written in Scala using Typelevel stack which presents weather info based on longitude and latitude.
Introduction
Scala is a general purpose statically typed language that seamlessly blends object-oriented and functional programming. Its expressive syntax and functional features, along with its JVM compatibility, makes it an attractive choice for developers. Although it has a steep learning curve, mastering Scala can lead to highly performant and stable software, especially in concurrent environments. All of this is what caught my eye many years ago when I first came across Scala.
Scala & Typelevel Stack
The Typelevel stack is a Scala based ecosystem/framework of modular abstractions that emphasize functional programming and Category Theory.
This article shares my experience creating an HTTP service using the Typelevel stack, in particular, http4s, cats, cats-effect, and circe. The service retrieves real-time weather information based on latitude and longitude passed to an HTTP GET endpoint. It also provides a high-level overview of how to leverage Typelevel projects to quickly set up a semi-production-ready JSON parsing HTTP service with integration tests and logging.
Acceptance Criteria
The HTTP Weather Service should adhere to the following requirements:
- Accepts latitude and longitude coordinates.
- Returns the short forecast for that area for Today (“Partly Cloudy” etc).
- Categorizes whether the temperature is “hot”, “cold”, or “moderate”.
- Uses the National Weather Service API Web Service as a data source.
- Comprehensive code coverage using
scalatest
.
Implementation
The interesting mix of Typelevel’s http4s, Cats, and Cats Effect libraries proved a different (and yet
a very rewarding) experience due to the functional programming style and mindset enforced. This contrasts immensely with
the traditional Java Spring Boot based Microservices codebases for which I have quite extensive professional experience
designing & developing in. The asynchronous capabilities of Cats Effect
, combined with the powerful functional
abstractions in Cats
(e.g. Monads
, Applicatives
, Show
, Traverse
, etc), have shown me how to fully leverage
Scala with Typelevel for specific distributed backend use cases.
Top-Level Singleton Object
Started out by using a top level singleton object aptly named WeatherServer
, to contain every method and expression,
which extends cats.effect.IOApp
:
|
|
HTTP GET Endpoint
For the specific HTTP GET endpoint, I used the http4s
library’s DSL convention for the routing:
|
|
Valid Coordinates
In order, to handle the edge case of invalid longitude & latitude coordinates, created the following helper method:
|
|
Classify Temperature
Wrote the following helper method to categorize the temperature as cold, moderate or hot:
|
|
Embedded HTTP Server
Used the embedded EmberServerBuilder
to serve this HTTP GET endpoint on port 8080:
|
|
Compilation & Bootstrap
Compile project using sbt
To compile the project, invoke sbt compile
:
|
|
Bootstrap/Run WeatherServer using sbt
To run locally, invoke sbt run
:
|
|
HTTP GET Endpoint
With the HTTP Service running locally, one can hit the HTTP GET endpoint passing in longitude and latitude for San Francisco:
http://localhost:8080/weather?latitude=37.7749&longitude=-122.4194
Which will return the following real-time weather information in JSON format:
|
|
Testing
Also, good software engineering never neglects weeding out all the edge cases through a proper unit/integration test!
A combination of cats.effect.IO
and http4s
inside my scalatest
helped make this test coverage not only
comprehensive but asynchronous:
Using scalatest
|
|
Running tests using sbt
Now, when invoking with sbt test
, seeing the tests passing was very gratifying (after jumping through a bunch of rabbit
holes, not only for the acceptance criteria, but dealing with the edge/corner cases that arose) as this was my first foray
using the Typelevel stack for creating a working prototype:
|
|
Afterthoughts
This was a good learning experience how Scala’s Typelevel stack can be used to create an HTTP API which sends requests
to external data sources, parses JSON, and returns asynchronous responses. Also, it demonstrates how to test Typelevel
code using the scalatest
library.
Future design considerations:
Refactor by extracting out the helper methods into a
WeatherInfo
trait and then extended that with a singleton object (e.g.RealTimeWeather
extendsWeatherInfo
) fromWeatherServer
.Refactor by extracting out the marshalled
circe
response case classes inside theWeatherServer
into a separate package namespace.Externalize the National Weather Service API URL to a configuration file.
Handle edge case of
ForecastProperties
containing an empty list ofPeriod
instances usingOption[List[Period]]
.Rewrite this using the Tagless Final design pattern.
An example of applying Tagless Final
, using the higher kind F[_]
type for the proposed WeatherInfo
trait would resemble this ADT-specific DSL:
|
|
By using the F[_]
higher kind type, you allow the caller to decide which effect type to use (e.g., IO, Future, etc.),
making the trait abstract over different computational effects. More on Tagless Final later…
GitHub Repository
The full code is available here: https://github.com/unnsse/WeatherService
Note: By no means, is this code production ready or even should be considered idiomatic Scala, this was just a mere exploratory exercise.