REST API and CRUD Operations with Akka HTTP
REST API and CRUD Operations with Akka HTTP
Introduction
In this post, we will be creating an Akka HTTP Application with GET, POST, PUT and DELETE APIs for CRUD operations on an Employee Domain class. All data will be kept in memory in a Vector.
Akka HTTP
The Akka HTTP modules implement a full server- and client-side HTTP stack on top of akka-actor and akka-stream. It’s not a web-framework but rather a more general toolkit for providing and consuming HTTP-based services.
Most important features that Akka HTTP provides are:
- Asynchronous processing
- Non blocking I/O operations
For more details on Akka HTTP, you can check: Akka HTTP Website.
Requirements to Run the Application:
1. Intellij IDE with SBT and Scala Plugins
In this tutorial I will help you to build an Akka HTTP Application in a few simple steps.
Run JAR by using java -jar command in the location: target/scala-2.12 of the Project folder location. For me it is:
Requirements to Run the Application:
1. Intellij IDE with SBT and Scala Plugins
In this tutorial I will help you to build an Akka HTTP Application in a few simple steps.
Step 1: Dependencies in build.sbt
Since the code is in Scala, I will be using sbt for build related setup. sbt (Scala Build Tool, formerly Simple Build Tool) is an open source build tool for Scala and Java projects, similar to Java's Maven and Ant. I am using the following in this tutorial:- ScalaVersion = 2.12.2
- AkkaVersion = 2.4.18
- AkkaHttpVersion = 10.0.6
- Json4sVersion = 3.5.2
These have to be set in the file: build.sbt
Here is the full build.sbt:
enablePlugins(JavaServerAppPackaging) name := "BasicAkkaHTTP" version := "1.0" organization := "com.aj" scalaVersion := "2.12.2" resolvers ++= Seq("Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/", Resolver.bintrayRepo("hseeberger", "maven")) libraryDependencies ++= { val AkkaVersion = "2.4.18" val AkkaHttpVersion = "10.0.6" val Json4sVersion = "3.5.2" Seq( "com.typesafe.akka" %% "akka-slf4j" % AkkaVersion, "com.typesafe.akka" %% "akka-http" % AkkaHttpVersion, "ch.qos.logback" % "logback-classic" % "1.2.3", "org.json4s" %% "json4s-native" % Json4sVersion, "org.json4s" %% "json4s-ext" % Json4sVersion, "de.heikoseeberger" %% "akka-http-json4s" % "1.16.0" ) } // Assembly settings mainClass in Global := Some("com.aj.basicakkahttp.Main") assemblyJarName in assembly := "BasicAkkaHTTP.jar"
Step 2: Application Configuration
Server host, port and logging related configurations are added in application.conf. Here is the full file application.conf:
akka { loglevel = INFO stdout-loglevel = INFO loggers = ["akka.event.slf4j.Slf4jLogger"] default-dispatcher { fork-join-executor { parallelism-min = 8 } } test { timefactor = 1 } } http { host = "0.0.0.0" host = ${?HOST} port = 4000 port = ${?PORT} }
Step 3: Create the Entity class
In this post, I am using a simple entity Employee with attributes: ID, NAME and DEPARTMENT Here is the Entity Model, Employee.scala:
package com.aj.basicakkahttp.domain case class Employee(id: String, name: String, department: String)
Step 4: Create the Service Layer
Service class creates a Vector to keep all the employee data in memory. It also has implementations for the following operations:
- Create an Employee
- Get/Read an Employee
- Update an Employee
- Delete an Employee
Here is the code for EmployeeService.scala:
package com.aj.basicakkahttp.service import com.aj.basicakkahttp.domain.{Employee, EmployeeUpdate} import scala.concurrent.{ExecutionContext, Future} class EmployeeService(implicit val executionContext: ExecutionContext) { var employees = Vector.empty[Employee] def createEmployee(employee: Employee): Future[Option[String]] = Future { employees.find(_.id == employee.id) match { case Some(q) => None case None => employees = employees :+ employee Some(employee.id) } } def getEmployee(id: String): Future[Option[Employee]] = Future { employees.find(_.id == id) } def updateEmployee(id: String, update: EmployeeUpdate): Future[Option[Employee]] = { def updateEntity(employee: Employee): Employee = { val title = update.name.getOrElse(employee.name) val text = update.department.getOrElse(employee.department) Employee(id, title, text) } getEmployee(id).flatMap { maybeEmployee => maybeEmployee match { case None => Future { None } case Some(employee) => val updatedEmployee = updateEntity(employee) deleteEmployee(id).flatMap { _ => createEmployee(updatedEmployee).map(_ => Some(updatedEmployee)) } } } } def deleteEmployee(id: String): Future[Unit] = Future { employees = employees.filterNot(_.id == id) } }
Step 5: Create Resource Class with RESTful APIs for CRUD operations on Employee Entity
The EmployeeResource class has the following APIs:
- GET API to get employee by ID
- POST API to create an employee
- PUT API to update an employee
- DELETE API to delete an employee by ID
Here is the code for EmployeeResource.scala:
package com.aj.basicakkahttp.resource import akka.http.scaladsl.server.Route import com.aj.basicakkahttp.domain.{Employee, EmployeeUpdate} import com.aj.basicakkahttp.routing.MyResource import com.aj.basicakkahttp.service.EmployeeService trait EmployeeResource extends MyResource { val employeeService: EmployeeService def employeeRoutes: Route = pathPrefix("employee") { pathEnd { post { entity(as[Employee]) { employee => completeWithLocationHeader( resourceId = employeeService.createEmployee(employee), ifDefinedStatus = 201, ifEmptyStatus = 409) } } } ~ path(Segment) { id => get { complete(employeeService.getEmployee(id)) } ~ put { entity(as[EmployeeUpdate]) { update => complete(employeeService.updateEmployee(id, update)) } } ~ delete { complete(employeeService.deleteEmployee(id)) } } } }
Step 6: Main Object and Rest Interface
Main Object is the Main Class and entry point of the application. Here we get the host and port from the config file application.conf and bind them to the APIs. Here is the code for Main.scala:
package com.aj.basicakkahttp import akka.actor._ import akka.http.scaladsl.Http import akka.stream.ActorMaterializer import akka.util.Timeout import com.typesafe.config.ConfigFactory import scala.concurrent.duration._ object Main extends App with RestInterface { val config = ConfigFactory.load() val host = config.getString("http.host") val port = config.getInt("http.port") implicit val system = ActorSystem("BasicAkkaHTTP") implicit val materializer = ActorMaterializer() implicit val executionContext = system.dispatcher implicit val timeout = Timeout(10 seconds) val api = routes Http().bindAndHandle(handler = api, interface = host, port = port) map { binding => println(s"REST interface bound to ${binding.localAddress}") } recover { case ex => println(s"REST interface could not bind to $host:$port", ex.getMessage) } }
In RestInterface.scala, we configure the routes and service used in this application. Here is the code for RestInterface.scala:
package com.aj.basicakkahttp import akka.http.scaladsl.server.Route import com.aj.basicakkahttp.resource.EmployeeResource import com.aj.basicakkahttp.service.EmployeeService import scala.concurrent.ExecutionContext trait RestInterface extends Resources { implicit def executionContext: ExecutionContext lazy val employeeService = new EmployeeService val routes: Route = employeeRoutes } trait Resources extends EmployeeResource
Run Application:
1. To run application in your IDE use:
2. To run JAR from Intellij:
Build jar by running assembly in SBT Tab in IntellijRun JAR by using java -jar command in the location: target/scala-2.12 of the Project folder location. For me it is:
D:\projects\gitprojects\BasicAkkaHTTP\target\scala-2.12>java -jar BasicAkkaHTTP.jar
API calls and results:
1. POST API to create an employee
JSON Request Body:{ "id": "1", "name": "Mark", "department": "Technology" }
http://localhost:4000/employee/1
3. PUT API to update an employee
http://localhost:4000/employee/1
JSON Request Body:
{ "name": "Jim", "department": "Accounts" }4. DELETE API to delete an employee by ID
http://localhost:4000/employee/1
Conclusion and GitHub link:
In this post I have shown you how you can create an Akka HTTP application and perform CRUD operations using RESTful APIs. The code used in this post is available on GitHub.
Learn the most popular and trending technologies like Machine Learning, Angular 5, Internet of Things (IoT), Akka HTTP, Play Framework, Dropwizard, Docker, Elastic Stack, Netflix Eureka, Netflix Zuul, Spring Cloud, Spring Boot, Flask and RESTful Web Service integration with MongoDB, Kafka, Redis, Aerospike, MySQL DB in simple steps by reading my most popular blog posts at Software Developer Central.
If you like my post, please feel free to share it using the share button just below this paragraph or next to the heading of the post. You can also tweet with #SoftwareDeveloperCentral on Twitter. To get a notification on my latest posts or to keep the conversation going, you can follow me on Twitter. Please leave a note below if you have any questions or comments.
Learn the most popular and trending technologies like Machine Learning, Angular 5, Internet of Things (IoT), Akka HTTP, Play Framework, Dropwizard, Docker, Elastic Stack, Netflix Eureka, Netflix Zuul, Spring Cloud, Spring Boot, Flask and RESTful Web Service integration with MongoDB, Kafka, Redis, Aerospike, MySQL DB in simple steps by reading my most popular blog posts at Software Developer Central.
If you like my post, please feel free to share it using the share button just below this paragraph or next to the heading of the post. You can also tweet with #SoftwareDeveloperCentral on Twitter. To get a notification on my latest posts or to keep the conversation going, you can follow me on Twitter. Please leave a note below if you have any questions or comments.
Comments
Post a Comment