Simple REST API with Play Framework
Last month, I spend a Saturday night learning to use the Play Framework which is a modern Java and Scala web application framework that has become increasingly famous due to its simplicity and good performance.
Write REST applications in Play is very easy. So let’s see how it works by creating a deliberately simple REST API to deal with a list of cars.
1. Creating a new Play Project
I installed Play through the Typesafe Activator following the instructions provided in this link. After that, I created a new Play project called “rest-example” by typing:
As shown below, after typing the command, the Play framework asks us whether we want to create a Scala or Java application. I chose the option 3 to create a Java application.
In the new created “rest-example” project, we see the following folders and files:
- app : The application code like Java classes and HTML files.
- conf : Configuration and routes definition files.
- project : The build scripts.
- public : Public files like images, CSS and javascripts.
- test : The tests (JUnit or Selenium) file.
Now you can launch the default application created by Play with the command:
When called by the first time this command will automatically download all dependencies, what can take a while. After that, you can access the application at http://localhost:9000.
2. Modeling the Problem
In this “rest-example” application, we will develop a simple REST API which allow users to query and post cars in a library. Each car will be identified by its model and manufacturer, and it’s also possible to specify a list of attributes. The Car
and Attribute
class look like this:
I put these classes inside the app folder, in a new package called model.
3. Cache
I didn’t want to create a database for this simple “experiment” with Play. So instead I decided to use a cache in my application to load some initial cars from a JSON file and to store new data.
The cars.json
file looks like this:
I used the Jackson library present in Play to convert the JSON payload to Java objects, and the Cache API to store the list of cars:
An important point about the cache is that by default the data stored will expire after some time. To avoid this you need to specify 0 as the expiration time.
I wanted the cache to be loaded only once at the application start. For that, I defined a Global object and its onStart()
method to load the cache on it:
Finally, to make this work, I had to change the application.global
property in the conf/application.conf
file with the package to my Global
class, since I didn’t create it in the route package (the app
directory):
4. GET operation
Now, let’s define an URL that returns in JSON the list of all cars in the cache. First, we need to define a new method getCars()
in the Application
class that is under app/controllers
. The Application
class is a controller. A controller is a class that extends the play.mvc.Controller
and groups several Java method that processes request parameters and produces a result to be sent to the client.
The Application.getCars()
method looks like this:
The method returns a play.mvc.Result
value, representing the HTTP response to be sent to the client. The ok(node)
constructs a 200 OK response containing in the body the list of cars in JSON.
The last step is to define the URL. This is done in the conf/routes
file, in which you must specify in this order:
- the request type (GET, POST, DELETE, etc);
- the URL;
- and the method to be executed in the call.
For example, I defined the cars
URL just as below:
After this, you can access the list of cars at http://localhost:9000/cars.
5. GET with parameters
It’s also possible to define some parameters to be passed thought the URL. For example, let’s change the getCars()
method so we can retrieve cars based on their manufacturer and model:
If the manufacturer and model are null, the method will return the list of all cars in the cache. So we must change the routes
file to pass the manufacturer and model parameters to the getCars()
method:
In the example above, if the manufacturer or model is not specified in the URL the default value null is used in the call.
It’s now possible to query the list of cars by manufacturer and/or model as shown below:
- http://localhost:9000/cars : retrieves all cars;
- http://localhost:9000/cars?manufacturer=Honda : retrieves all cars which manufacturer is “Honda”;
- http://localhost:9000/cars?model=Civic : retrieves all cars which model is “Civic”;
- http://localhost:9000/cars?manufacturer=Honda&model=Civic : retrieves all cars which manufacturer is “Honda” and model is “Civic”.
6. POST and PUT
The last thing that I wanted to try was to create some POST and PUT operations to be possible to add new cars to the cache or update the existing ones. For this, I created two new methods in the Application
class:
To parse the JSON payload you must annotate the method with the JSON Body Parser and call the request().body().asJson()
to retrieve the JsonNode
object. Next, you can use the ObjectMapper
to convert from a JsonNode
object to a Car
object.
The two methods perform some validations before try to add or update a car. They check if the model and manufacturer are not null, and if already exist or not a car in the cache with the same manufacturer and model.
In the routes
file the URLs were defined as follow:
So to add a new car to the cache you need to send a HTTP POST to http://localhost:9000/cars with the JSON payload that represents the new car in the HTTP body. Following is an example using cURL:
And to update an existent car you must send a HTTP PUT to http://localhost:9000/cars/<manufacturer>/<model> with the car’s new data in the HTTP body. The example below will update the car that we just created:
7. Source Code
You can find the source code for this simple REST API in my GitHub: https://github.com/marianafranco/rest-example