Since it began life a little over three years ago, the Scalatra web micro-framework has evolved into a lightweight but full-featured model-view-controller (MVC) framework with a lively community behind it. Scalatra started out as a port of Ruby's popular Sinatra DSL to the Scala language. Since then the two systems have evolved independently, with Scalatra gaining capabilities such as an Atmosphere integration and Akka support.
It's been used by BBC Future Media for their Linked Data Writer API, managing large datasets in a scalable and manageable way, and has also been used by gov.uk.
One of the things that Scalatra's been most successful at is the construction of APIs. Over the past several years, REST APIs have become the lifeblood of the web. A relatively recent addition to Scalatra's capabilities is an integration with the Swagger toolset, which is produced by the folks at Wordnik.
What is Swagger?
Swagger is a specification which allows you to quickly define the functionality of a REST API using JSON documents. But it's more than just a spec. It provides automatic generation of interactive API docs, client-side code generation in multiple languages, and server-side code generation in Java and Scala.
Although they're the most eye-catching component of the project and will impress users, the docs produced by Swagger are also great for fostering communication between API producers and consumers during the API design phase.
Let's take a look at how it all works. We'll build out a small REST API using Scalatra, then use Swagger to document our routes.
The first thing you'll need to do is install Scalatra. The easiest way to do this is by following the installation instructions at the Scalatra website. See the notes at the bottom for setting up Eclipse or IntelliJ if you use those IDEs, but you should be able to do this tutorial in any text editor.
Once you've got a JVM installed, along with cs
, giter8
, and sbt
, you'll be able to generate a new Scalatra project.
Getting started
Type this at the command line:
g8 scalatra/scalatra-sbt --branch develop
You'll be asked a series of questions about your project. Answer like this:
organization [com.example]: package [com.example.app]: com.example.swagger.sample name [scalatra-sbt-prototype]: flowershop servlet_name [MyScalatraServlet]: FlowersController scala_version [2.9.2]: version [0.1.0-SNAPSHOT]:
Hit to accept the defaults for the organization, scala_version, and version questions.
Once you answer the last question, a full Scalatra project will be generated. Let's check that it works. Change directory into the flowershop
folder, and run Scala's simple build tool by typing:
cd flowershop
sbt
You'll need the latest sbt 0.12.1 for this. sbt 0.12.0 will work, just downgrade the version number in the file project/build.properties
.
sbt will take care of downloading all of Scalatra's dependencies. This can take several minutes when you're doing it for the first time, as you're getting a full Scala development environment, an embedded webserver (jetty), Scalatra itself, and several companion libraries.
When sbt finishes setting everything up, you should be able to start the application by typing the following at the sbt prompt (which looks like a ">").
container:start
This will start jetty on http://localhost:8080. Visit that URL in your browser, and you should see a Hello World application.
You don't want to have to manually recompile your app and restart Jetty whenever you make a code change, so type this at the sbt prompt:
~; copy-resources; aux-compile
This tells sbt to automatically recompile and reload the application whenever you change a file.
So, now we've got a controller for our flower shop. Let's set up a RESTful interface allowing us to browse flowers.
Open up the FlowersController.scala
file found in src/main/scala/com/example/swagger/sample
. What you start out with is a very simple generated controller. It's got a "hello world" action mounted on the application's root path get("/")
, which can be accessed using an HTTP GET to the path "/"
. It's also got a way to handle 404s, and Scalate templating, which we don't need for our API.
Scalatra allows you to easily add functionality to your controllers by mixing in Scala traits to your class definitions. Let's slim things down a tiny bit by removing the ScalateSupport
, since we don't need HTML templating support for our API. Delete the with ScalateSupport
part of the class definition, so it looks like this:
class FlowersController extends ScalatraServlet {
You can also remove the import scalate.ScalateSupport
, it won't be needed.
You can remove the notFound and get("/") actions as well. You'll be left with an empty Scalatra controller:
package com.example.swagger.sample import org.scalatra._ class FlowersController extends ScalatraServlet { }
As you make these changes and save your files, check sbt's output in the terminal. You should see your source code automatically recompiling itself and reloading the application. The terminal output will turn green once you've slimmed down your controller. You've now taken all of the routes out of FlowersController, so you won't be able to see anything in your browser.
Setting up the data model
Let's get some data set up. Since we want to focus on learning Swagger, we won't attempt to actually persist anything in this tutorial. We can use Scala case classes to simulate a data model instead. If you'd like to see how to find out how to set up ScalaQuery, a Scala ORM, check out Jos Dirksen's tutorial at SmartJava.
First, we'll need a flower model. Add a new directory in src/main/scala/com/example/swagger/sample
, and call it models
. Then put the following code in a new file, Models.scala, in there:
Models.scala
package com.example.swagger.sample.models // A Flower object to use as a faked-out data model case class Flower(slug: String, name: String)
A Scala case class
automatically adds getters and setters to the class definition, so we get a lot of functionality here without a lot of boilerplate.
Let's add some flower data.
Make a data namespace by adding a new data
directory inside src/main/scala/com/example/swagger/sample
. Then add a new file in that directory, calling it FlowerData.scala
.
The contents of the file should look like this:
FlowerData.scala
package com.example.swagger.sample.data import com.example.swagger.sample.models._ object FlowerData { /** * Some fake flowers data so we can simulate retrievals. */ var all = List( Flower("yellow-tulip", "Yellow Tulip"), Flower("red-rose", "Red Rose"), Flower("black-rose", "Black Rose")) }
That gives us enough to work with in terms of data that we can at least demonstrate our API's functionality.
Let's make a new controller action to retrieve flowers. Add some imports so that we get access to our models and data in the FlowersController class, by adding this at the top, after the package definition:
// Our models import com.example.swagger.sample.models._ // Fake flowers data import com.example.swagger.sample.data._
Retrieving flowers
Now let's make our first API method, a Scalatra action which lets clients browse flowers. Drop this into the body of the FlowersController class:
get("/"){ FlowerData.all }
It doesn't look like much, but this is faking out data retrieval for us, by calling the all
method of the FlowerData
object we just defined. This is broadly equivalent to calling a static method in Java or C#, or a class method in Ruby.
If you take a look at http://localhost:8080/ in your browser, you should see the following result:
List(Flower(yellow-tulip,Yellow Tulip), Flower(red-rose,Red Rose), Flower(black-rose, Black Rose))
Scalatra has found all the flowers for us and returned the data. Looking at what we've got so far, though, it's not a very descriptive API. What resource is actually being retrieved? It's not possible to tell by looking at the URL. Let's change that.
Setting the mount path for better API clarity
Every Scalatra application has a file called ScalatraBootstrap.scala
, located in the src/main/scala
directory. This file allows you to mount your controllers at whatever url paths you want. If you open yours right now, it'll look something like this:
import com.example.swagger.sample._ import org.scalatra._ import javax.servlet.ServletContext class ScalatraBootstrap extends LifeCycle { override def init(context: ServletContext) { context.mount(new FlowersController, "/*") } }
Let's change it a bit, adding a route namespace to the FlowersController:
import com.example.swagger.sample._ import org.scalatra._ import javax.servlet.ServletContext class ScalatraBootstrap extends LifeCycle { override def init { context.mount(new FlowersController, "/flowers") } }
The only change was to replace the "/*" mount point with "/flowers". Easy enough. Let's make sure it works. Hit the url http://localhost:8080/flowers in your browser, and you should once again see the same results as before:
List(Flower(yellow-tulip,Yellow Tulip), Flower(red-rose,Red Rose), Flower(black-rose, Black Rose))
This is a much more descriptive URL path. Clients can now understand that they're operating on a flower
resource.
Automatic JSON output for API actions
Take a closer look at the output. What's going on here? Scalatra has converted the FlowerData.all
value to a string and rendered its Scala source representation as the response. This is the default behaviour, but in fact we don't want things to work this way - we want to use JSON as our data interchange format.
Let's get that working. Scalatra 2.2 includes some new JSON handling capabilities which makes this a snap.
In order to use Scalatra's JSON features, we'll need to add a couple of library dependencies so that our application can access some new code. In the root of your generated project, you'll find a file called build.sbt
. Open that up, and add the following two lines to the libraryDependencies
sequence, after the other scalatra-related lines:
"org.scalatra"% "scalatra-json"% "2.2.0-SNAPSHOT", "org.json4s"%% "json4s-jackson"% "3.0.0",
build.sbt
is somewhat equivalent to a maven pom.xml
in Java, or a Gemfile
in Ruby, insofar as it keeps track of all your project's dependencies and can take care of downloading them for you.
Restart sbt to download the new jars. You can do so by first hitting the "enter" key (to stop automatic recompilation), and then typing exit
at the sbt prompt. Then type sbt
again. You should see some messages telling you that sbt is downloading the new dependencies, and then you'll be back at the prompt. Start the container and recompilation again:
container:start ~; copy-resources; aux-compile
Add the following imports to the top of your FlowersController file, in order to make the new JSON libraries available:
// JSON-related libraries import scala.collection.JavaConverters._ import org.json4s.{DefaultFormats, Formats} // JSON handling support from Scalatra import org.scalatra.json._
Now we can add a bit of magic to the FlowersController. Putting this line of code right underneath the controller class definition will allow your controller to automatically convert Scalatra action results to JSON:
// Sets up automatic case class to JSON output serialization, required by // the JValueResult trait. protected implicit val jsonFormats: Formats = DefaultFormats
Just like its Sinatra forebear, Scalatra has a rich set of constructs for running things before and after requests to your controllers. A before
filter runs before all requests. Add a before
filter to set all output for this controller to set the content type for all action results to JSON:
// Before every action runs, set the content type to be in JSON format. before() { contentType = formats("json") }
Now mix JacksonJsonSupport
and JValueResult
into your servlet so your controller declaration looks like this:
class FlowersController extends ScalatraServlet with JacksonJsonSupport with JValueResult {
Your code should compile again at this point. Refresh your browser at http://localhost:8080/flowers, and suddenly the output of your /
action has changed to JSON:
[{"slug":"yellow-tulip","name":"Yellow Tulip"},{"slug":"red-rose","name":"Red Rose"},{"slug":"black-rose","name":"Black Rose"}]
The JValueResult
and JsonJacksonSupport
traits which we mixed into the controller, combined with the implicit val jsonFormats
, are now turning all Scalatra action result values into JSON.
Making the flowers API searchable
Next, let's make our API searchable. We want to be able to search for flowers by name and get a list of results matching the query. The easiest way to do this is with some pattern matching inside the /
in our controller.
Currently that route looks like this:
get("/"){ FlowerData.all }
We can change it to read a query string parameter, and search inside our list of flowers.
/* * Retrieve a list of flowers */ get("/"){ params.get("name") match { case Some(name) =>FlowerData.all filter (_.name.toLowerCase contains name.toLowerCase()) case None =>FlowerData.all } }
Scalatra can now grab any incoming ?name=foo
parameter off the query string, and make it available to this action as the variable name
, then filter the FlowerData list for matching results.
If you refresh your browser at http://localhost:8080/flowers, you should see no change - all flowers are returned. However, if you point your browser at http://localhost:8080/flowers?name=rose, you'll see only the roses.
Retrieving a single flower by its slug
The last controller method we'll create for the moment is one that retrieves a specific flower. We can easily retrieve a flower by its slug, like this:
get("/:slug") { FlowerData.all find (_.slug == params("slug")) match { case Some(b) =>b case None =>halt(404) } }
Once again, we're using Scala's pattern matching to see whether we can find a matching slug. If we can't find the desired flower, the action returns a 404 and halts processing.
You can see the API's output by pointing your browser at a slug, e.g. http://localhost:8080/flowers/yellow-tulip
{"slug":"yellow-tulip","name":"Yellow Tulip"}
The nice thing is that since our before()
filter runs on each and every action, the JSON format converter is still operating on our output. We get automatic JSON support with no extra effort. Sweet.
Interface-driven development using Swagger
At this point, we've got the beginnings of a REST API. It defines two actions, and offers a way for API clients to see what flowers are available in our flower shop. With our desired functionality achieved, we could stop here. But we're missing two things: human-readable documentation and client integration code. And without these, the API is a lot less useful than it could be.
An API is a way for machines to exchange data, but the process of designing and building an API also requires a lot of communication between people. The best API designs happen when the API's users have a way to get involved and detail what it is they need, and the API implementers boil those conversations down into an interface that works for the desired user stories or use cases. Historically, the fact that you've needed to use cURL or read WSDL to understand what an API does has severely limited the ability of non-technical people to participate in the API design process. The technical complexity of just making a connection has masked the fact that, at their core, the concepts inherent in a REST API are not particularly hard to understand.
Making the API's methods, parameters, and responses visible, in an engaging, easy to understand way, can transform the process of building REST APIs. The people at Wordnik, the word meanings site, have built a toolset called Swagger, which can help with this.
Swagger is a bunch of different things. It's a specification for documenting the behaviour of a REST API - the API's name, what resources it offers, available methods and their parameters, and return values. The specification can be used in a standalone way to describe your API using simple JSON files.
The Swagger resources file
If you want to, you can write a Swagger JSON description file by hand. A Swagger resource description for our FlowersController might look like this (don't bother doing so, though, because we'll see how to automate this in a moment):
{"basePath":"http://localhost:8080","swaggerVersion":"1.0","apiVersion":"1","apis":[{"path":"/api-docs/flowers.{format}","description":"The flowershop API. It exposes operations for browing and searching lists of flowers"}]}
This file describes what APIs we're offering. Each API has its own JSON descriptor file which details what resources it offers, the paths to those resources, required and optional parameters, and other information.
A sample Swagger resource file
The descriptor for our flower
resource might look something like this:
{"resourcePath":"/","listingPath":"/api-docs/flowers","description":"The flowershop API. It exposes operations for browing and searching lists of flowers","apis":[{"path":"//","description":"","secured":true,"operations":[{"httpMethod":"GET","responseClass":"List[Flower]","summary":"Show all flowers","notes":"Shows all the flowers in the flower shop. You can search it too.","deprecated":false,"nickname":"getFlowers","parameters":[{"name":"name","description":"A name to search for","required":false,"paramType":"query","allowMultiple":false,"dataType":"string"}],"errorResponses":[]}]},{"path":"//{slug}","description":"","secured":true,"operations":[{"httpMethod":"GET","responseClass":"Flower","summary":"Find by slug","notes":"Returns the flower for the provided slug, if a matching flower exists.","deprecated":false,"nickname":"findBySlug","parameters":[{"name":"slug","description":"Slug of flower that needs to be fetched","required":true,"paramType":"path","allowMultiple":false,"dataType":"string"}],"errorResponses":[]}]}],"models":{"Flower":{"id":"Flower","description":"Flower","properties":{"name":{"description":null,"enum":[],"required":true,"type":"string"},"slug":{"description":null,"enum":[],"required":true,"type":"string"}}}},"basePath":"http://localhost:8080","swaggerVersion":"1.0","apiVersion":"1"}
These JSON files can then be offered to a standard HTML/CSS/JavaScript client to make it easy for people to browse the docs. It's extremely impressive - take a moment to view the Swagger Pet Store example. Click on the route definitions to see what operations are available for each resource. You can use the web interface to send real test queries to the API, and view the API's response to each query.
Swagger language and framework integrations
Let's get back to the spec files. In addition to enabling automatic documentation as in the Pet Store example, these JSON files allow client and server code to be automatically generated, in multiple languages.
This means that unless you want to, you don't need to generate these JSON files by hand. There are integrations with a wide variety of frameworks, including ASP.NET, express, fubumvc, JAX-RS, Play, Ruby, and Spring MVC.
The framework integrations allow you to annotate the code within your RESTful API in order to automatically generate JSON descriptors which are valid Swagger specs. This means that once you annotate your API methods, you get some very useful (and pretty) documentation capabilities for free, using the swagger-ui. You also get the ability to generate client and server code in multiple languages, using the swagger-codegen project. Client code can be generated for Flash, Java, JavaScript, Objective-C, PHP, Python, Python3, Ruby, or Scala.
Setting up the Scalatra Flower Shop with Swagger
Let's annotate our Scalatra flowershop with Swagger, in order to auto-generate runnable API documentation.
Add the dependencies
First, add the Swagger dependencies to your build.sbt
file:
"com.wordnik"% "swagger-core_2.9.1"% "1.1-SNAPSHOT", "org.scalatra"% "scalatra-swagger"% "2.2.0-SNAPSHOT",
Exit your sbt console and once again type sbt
in the top-level directory of your application in order to pull in the dependencies. Then run container:start
and ~; copy-resources; aux-compile
to get code reloading going again.
You'll now need to import Scalatra's Swagger support into your FlowersController:
// Swagger support import org.scalatra.swagger._
Auto-generating the resources.json spec file
Any Scalatra application which uses Swagger support must implement a Swagger controller. Those JSON specification files, which we'd otherwise need to write by hand, need to be served by something, after all. Let's add a standard Swagger controller to our application. Drop this code into a new file next to your FlowersController.scala. You can call it FlowersSwagger.scala
FlowersSwagger.scala
package com.example.swagger.sample import org.scalatra.swagger.{JacksonSwaggerBase, Swagger, SwaggerBase} import org.scalatra.ScalatraServlet import com.fasterxml.jackson.databind._ import org.json4s.jackson.Json4sScalaModule import org.json4s.{DefaultFormats, Formats} class ResourcesApp(implicit val swagger: Swagger) extends ScalatraServlet with JacksonSwaggerBase class FlowersSwagger extends Swagger("1.0", "1")
That code basically gives you a new controller which will automatically produce Swagger-compliant JSON specs for every Swaggerized API method in your application.
The rest of your application doesn't know about it yet, though. In order to get everything set up properly, you'll need to change your ScalatraBootstrap file so that the container knows about this new servlet. Currently it looks like this:
import com.example.swagger.sample._ import org.scalatra._ import javax.servlet.ServletContext class ScalatraBootstrap extends LifeCycle { override def init(context: ServletContext) { context.mount(new FlowersController, "/flowers") } }
Change it to look like this:
class ScalatraBootstrap extends LifeCycle { implicit val swagger = new FlowersSwagger override def init(context: ServletContext) { context mount(new FlowersController, "/flowers") context mount (new ResourcesApp, "/api-docs") } }
Adding SwaggerSupport to the FlowersController
Then we can add some code to enable Swagger on your FlowersController. Currently, your FlowersController declaration should look like this:
class FlowersController extends ScalatraServlet with JacksonJsonSupport with JValueResult {
Let's add the SwaggerSupport trait, and also make the FlowerController aware of swagger in its constructor.
class FlowersController(implicit val swagger: Swagger) extends ScalatraServlet with JacksonJsonSupport with JValueResult with SwaggerSupport {
In order to make our application compile again, we'll need to add a name and description to our FlowersController. This allows Swagger to inform clients what our API is called, and what it does. You can do this by adding the following code to the body of the FlowersController class:
override protected val applicationName = Some("flowers") protected val applicationDescription = "The flowershop API. It exposes operations for browsing and searching lists of flowers, and retrieving single flowers."
That's pretty much it for setup. Now we can start documenting our API's methods.
Annotating API methods
Swagger annotations are quite simple in Scalatra. You decorate each of your routes with a bit of information, and Scalatra generates the JSON spec for your route.
Let's do the get("/")
route first.
Right now, it looks like this:
get("/"){ params.get("name") match { case Some(name) =>FlowerData.all filter (_.name.toLowerCase contains name.toLowerCase) case None =>FlowerData.all } }
We'll need to add some information to the method in order to tell Swagger what this method does, what parameters it can take, and what it responds with.
get("/", summary("Show all flowers"), nickname("getFlowers"), responseClass("List[Flower]"), parameters(Parameter("name", "A name to search for", DataType.String, paramType = ParamType.Query, required = false)), endpoint(""), notes("Shows all the flowers in the flower shop. You can search it too.")){ params.get("name") match { case Some(name) =>FlowerData.all filter (_.name.toLowerCase contains name.toLowerCase) case None =>FlowerData.all } }
Let's go through the annotations in detail.
The summary
and notes
should be human-readable messages that you intend to be read by developers of API clients. The summary is a short description, while the notes should offer a longer description and include any noteworthy features which somebody might otherwise miss.
The nickname
is intended as a machine-readable key which can be used by client code to identify this API action - it'll be used, for instance, by swagger-ui to generate method names. You can call it whatever you want, but make sure you don't include any spaces in it, or client code generation will probably fail - so "getFlowers" or "get_flowers" is fine, "get flowers" isn't.
The responseClass
is essentially a type annotation, so that clients know what data types to expect back. In this case, clients should expect a List of Flower objects.
The parameters
details any parameters that may be passed into this route, and whether they're supposed to be part of the path, post params, or query string parameters. In this case, we define an optional query string parameter called name
, which matches what our action expects.
Lastly, the endpoint
annotation defines any special parameter substitution or additional route information for this method. This particular route is pretty straightforward, so we can leave this blank.
We can do the same to our get(/:slug)
route. Change it from this:
get("/:slug") { FlowerData.all find (_.slug == params("slug")) match { case Some(b) =>b case None =>halt(404) } }
to this:
get("/:slug", summary("Find by slug"), nickname("findBySlug"), responseClass("Flower"), endpoint("{slug}"), notes("Returns the flower for the provided slug, if a matching flower exists."), parameters( Parameter("slug", "Slug of flower that needs to be fetched", DataType.String, paramType = ParamType.Path))) { FlowerData.all find (_.slug == params("slug")) match { case Some(b) =>b case None =>halt(404) } }
The Swagger annotations here are mostly similar to those for the get("/")
route. There are a few things to note.
The endpoint
this time is defined as {slug}
. The braces tell Swagger that it should substitute the contents of a path param called {slug}
into any generated routes (see below for an example). Also note that this time, we've defined a ParamType.Path
, so we're passing the slug
parameter as part of the path rather than as a query string. Since we haven't set the slug
parameter as required = false
, as we did for the name
parameter in our other route, Swagger will assume that slugs are required.
Now let's see what we've gained.
Adding Swagger support to our application, and the Swagger annotations to our FlowersController, means we've got some new functionality available. Check the following URL in your browser:
http://localhost:8080/api-docs/resources.json
You should see an auto-generated Swagger description of available APIs (in this case, there's only one, but there could be multiple APIs defined by our application and they'd all be noted here):
{"basePath":"http://localhost:8080","swaggerVersion":"1.0","apiVersion":"1","apis":[{"path":"/api-docs/flowers.{format}","description":"The flowershop API. It exposes operations for browing and searching lists of flowers"}]}
Now for the wonderful part.
Browsing your API using swagger-ui
If you browse to http://petstore.swagger.wordnik.com/, you'll see the default Swagger demo application - a Pet Store - and you'll be able to browse its documentation. One thing which may not be immediately obvious is that we can use this app to browse our local Flower Shop as well.
The Pet Store documentation is showing because http://petstore.swagger.wordnik.com/api/resources.json is entered into the URL field by default.
Paste your Swagger resource descriptor URL - http://localhost:8080/api-docs/resources.json
- into the URL field, delete the "special-key" key, then press the "Explore" button. You'll be rewarded with a fully Swaggerized view of your API documentation. Try clicking on the "GET /flowers" route to expand the operations underneath it, and then entering the word "rose" into the input box for the "name" parameter. You'll be rewarded with JSON output for the search method we defined earlier.
Also note that the swagger-ui responds to input validation: you can't try out the /flowers/{slug}
route without entering a slug, because we've marked that as a required parameter in our Swagger annotations. Note that when you enter a slug such as "yellow-tulip", the "{slug}"
endpoint annotation on this route causes the swagger-ui to fire the request as /flowers/yellow-tulip
.
If you want to host your own customized version of the docs, you can of course just download the swagger-ui code from Github and drop it onto any HTTP server.
A note on cross-origin security
Interestingly, you are able to use the remotely-hosted documentation browser at http://petstore.swagger.wordnik.com to browse an application on http://localhost. Why is this possible? Shouldn't JavaScript security restrictions have come into play here?
The reason it works is that Scalatra has Cross-Origin Resource Sharing (CORS) support built-in, allowing cross-origin JavaScript requests by default for all requesting domains. This makes it easy to serve JS API clients - but if you want, you can lock down requests to specific domains using Scalatra's CorsSupport trait. See the Scalatra Helpers documentation for more.
Conclusion
Without much in the way of boilerplate code, you've now constructed a simple REST API, set up model-class to JSON output functionality, and auto-generated API documentation by annotating your Scalatra routes with Swagger information.
This is one way to use Swagger, but the Wordniks swagger differently: rather than starting with the API and using Swagger to just generate the docs, they start out by writing the JSON descriptor files by hand. They then look at the API using the HTML docs browser, and have all the parties who are interested in the API sit down and discuss what's needed. After changing the JSON files based on the discussion, they use swagger-codegen
to generate the client and server code. This is called interface driven development, and it's well worth a look. With its ease of use, multi-framework integration, and innovative way of involving people in the design process, Swagger is at the forefront of REST API construction tools.
The code
You can download and run a working version of this application by installing Scalatra as detailed at the start of this tutorial, doing a git clone https://github.com/futurechimp/flowershop.git
, and running sbt
in the top-level of the project.
About the author
Dave Hrycyszyn is Technical Director at Head London, a digital innovation agency in the UK. He is passionate about APIs and application architectures, and is a member of the team working on the Scalatra micro-framework, which has been used by LinkedIn, the BBC, the Guardian newspaper, and gov.uk. He has a keen interest in organizations and institutions, and the interplay between social structures and software.