Skip to Content
Course content

54: An Overview of the Play Framework

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Think of the Play Framework like a well-run professional kitchen. When a customer walks in, they don't just wander into the pantry and start grabbing ingredients. There's a system. First, they look at a menu to see what's available. Then, they tell a waiter what they want. The waiter doesn't cook the food; they just take the order and tell the chef exactly what needs to happen. Finally, the food is plated beautifully before it hits the table. If the waiter tried to cook, or the chef tried to take orders, the whole place would crash during the dinner rush.

In Play, this is exactly how your web requests are handled. The "menu" is your routes file. The "waiter" is the Controller. The "chef" is your service layer or model where the actual Scala logic lives. And the "plating" is the Twirl template engine that turns your data into HTML.

The Traffic Cop: The Routes File

Unlike many frameworks where you scatter annotations like @Get("/home") all over your classes, Play uses a centralized routes file. I personally find this much cleaner because I can see the entire map of my application in one glance without hunting through twenty different files.

GET  /books/:isbn   controllers.BookController.details(isbn: String)

In this line, we're telling Play: "If a GET request comes in for /books/ followed by some text, hand that text over to the details method in the BookController." It's a direct mapping that keeps your controllers focused on logic rather than configuration.

Handling the Request in the Controller

The Controller is where you'll spend a lot of your time. Its only job is to receive the request, call the right service, and return a result. Since we're in Scala, we aren't blocking threads. We use Action and Future to make sure our app can handle thousands of concurrent users without freezing up.

Let's look at a specific example. Imagine we're building a digital library. Here is how a basic controller action looks:

class BookController @Inject()(cc: ControllerComponents, bookService: BookService) extends AbstractController(cc) {
  def details(isbn: String) = Action.async {
    bookService.findBookByIsbn(isbn).map {
      case Some(book) => Ok(views.html.bookDetail(book))
      case None       => NotFound(views.html.errorPage("Book not found"))
    }
  }
}

Notice the @Inject()? Play uses Dependency Injection heavily. I don't manually instantiate BookService; I just tell Play I need it, and the framework provides it. Also, notice the Action.async. We're returning a Future[Result], which means the server can go do other things while the database is searching for that ISBN.

Plating the Data with Twirl

Now, you can't just send a Scala Case Class to a browser; the browser only speaks HTML. This is where Twirl comes in. Twirl templates are actually compiled into Scala code, which means if you make a typo in your template, the compiler will catch it before you even run the app. This is a massive advantage over frameworks that use plain text templates (like JSP or Thymeleaf) where you only find out something is broken when the page crashes in production.

A Twirl template (bookDetail.scala.html) looks like this:

@(book: Book)

@book.title

Author: @book.author

ISBN: @book.isbn

Back to Library

The @ symbol is the magic switch. Everything else is standard HTML; anything starting with @ is executed as Scala code. It's a tight, type-safe loop from the route to the controller and finally to the view.




📋 Practical Task

Build a "User Profile" Route and View

You are tasked with adding a user profile feature to an existing Play application. You need to create the plumbing that allows a user to be looked up by their username.

Requirements:

  • The Route: Add a route to the conf/routes file that accepts a GET request at /user/:username and maps it to a method called profile in UserController.
  • The Controller: In the UserController, implement the profile method. It should call a mock service userService.getUser(username) (which returns a Future[Option[User]]).
    • If the user exists, return an Ok response rendering a template called userProfile.scala.html.
    • If the user does not exist, return a NotFound response.
  • The Template: Create a basic Twirl template userProfile.scala.html that takes a User object and displays the user's displayName and email inside <h2> and <p> tags respectively.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.