scalaintermediate
Play Framework REST Controller
Build REST APIs with Play Framework: routes, JSON serialization, async actions, and error handling.
scalaPress ⌘/Ctrl + Shift + C to copy
package controllers
import javax.inject.*
import play.api.mvc.*
import play.api.libs.json.*
import scala.concurrent.{ExecutionContext, Future}
case class Todo(id: Long, title: String, completed: Boolean)
object Todo:
given Format[Todo] = Json.format[Todo]
@Singleton
class TodoController @Inject()(
val controllerComponents: ControllerComponents
)(using ec: ExecutionContext) extends BaseController:
// In-memory store
private var todos = List(
Todo(1, "Learn Scala", false),
Todo(2, "Build API", false),
Todo(3, "Deploy app", false)
)
private var nextId = 4L
// GET /todos
def list(): Action[AnyContent] = Action { implicit request =>
Ok(Json.toJson(todos))
}
// GET /todos/:id
def get(id: Long): Action[AnyContent] = Action {
todos.find(_.id == id) match
case Some(todo) => Ok(Json.toJson(todo))
case None => NotFound(Json.obj("error" -> s"Todo $id not found"))
}
// POST /todos
def create(): Action[JsValue] = Action(parse.json) { request =>
request.body.validate[Todo].fold(
errors => BadRequest(Json.obj("error" -> JsError.toJson(errors))),
todo =>
val newTodo = todo.copy(id = nextId)
nextId += 1
todos = todos :+ newTodo
Created(Json.toJson(newTodo))
)
}
// PUT /todos/:id
def update(id: Long): Action[JsValue] = Action(parse.json) { request =>
request.body.validate[Todo].fold(
errors => BadRequest(Json.obj("error" -> JsError.toJson(errors))),
updated =>
todos.find(_.id == id) match
case Some(_) =>
todos = todos.map(t => if t.id == id then updated.copy(id = id) else t)
Ok(Json.toJson(updated.copy(id = id)))
case None =>
NotFound(Json.obj("error" -> s"Todo $id not found"))
)
}
// DELETE /todos/:id
def delete(id: Long): Action[AnyContent] = Action {
todos.find(_.id == id) match
case Some(_) =>
todos = todos.filterNot(_.id == id)
NoContent
case None =>
NotFound(Json.obj("error" -> s"Todo $id not found"))
}
// Async action
def asyncGet(id: Long): Action[AnyContent] = Action.async {
Future {
todos.find(_.id == id) match
case Some(todo) => Ok(Json.toJson(todo))
case None => NotFound(Json.obj("error" -> s"Todo $id not found"))
}
}
// conf/routes:
// GET /todos controllers.TodoController.list()
// GET /todos/:id controllers.TodoController.get(id: Long)
// POST /todos controllers.TodoController.create()
// PUT /todos/:id controllers.TodoController.update(id: Long)
// DELETE /todos/:id controllers.TodoController.delete(id: Long)Sponsored
Railway
Use Cases
- REST API development
- CRUD operations with Play
- JSON API endpoints
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
scalaintermediate
HTTP Server with http4s
Build HTTP servers with http4s: routes, middleware, JSON, and streaming responses.
Best for: REST API development with http4s
#scala#http4s
scalabeginner
Scala Hello World Application
Create a basic Scala application with main method, string interpolation, and val/var basics.
Best for: Getting started with Scala
#scala#basics
scalabeginner
Pattern Matching Fundamentals
Use Scala pattern matching with guards, type patterns, tuple patterns, and nested extractors.
Best for: Control flow with pattern matching
#scala#pattern-matching
scalabeginner
Case Classes and Objects
Define immutable data with case classes: copy, equality, destructuring, and companion objects.
Best for: Domain modeling with immutable data
#scala#case-class