ZIO Tutorial
This tutorial demonstrates using ZIO within a Spring Boot application.
The application retrieves a message from an H2 database via a Spring Data JPA repository and returns it in an HTTP response.
Maven dependency
Add ZIO support alongside the Spring Boot starter:
<dependency>
<groupId>net.officefloor.scala</groupId>
<artifactId>officescala_zio</artifactId>
</dependency>
Application class
Register DefaultScalaModule so that Jackson can serialise and deserialise Scala case classes used for the request and response:
@SpringBootApplication
class Application {
@Bean
def scalaJacksonModule(): JacksonModule = DefaultScalaModule
}
object Application extends App {
SpringApplication.run(classOf[Application], args: _*)
}
ZIO service
The ZIO effect uses ZIO.serviceWithZIO to retrieve a message from the injected MessageRepository environment:
object MessageService {
def getMessage(id: Int): ZIO[MessageRepository, Throwable, Message] =
ZIO.serviceWithZIO[MessageRepository](repository =>
ZIO.attempt(repository findById id orElseThrow(() => new NoSuchElementException(s"No message by id $id"))))
}
This is tested without any HTTP or Spring infrastructure:
class MessageServiceTest {
@Test
def retrieveMessage(): Unit = {
val retrieve = for {
m <- MessageService.getMessage(1)
} yield m
val message = Unsafe.unsafe { implicit unsafe =>
runtime(1, "Hello World").unsafe.run(retrieve).getOrThrowFiberFailure()
}
assertEquals("Hello World", message.getContent)
}
def runtime(id: Int, content: String): Runtime[MessageRepository] =
Runtime(ZEnvironment[MessageRepository](new TestMessageRepository(id, content)), FiberRefs.empty, RuntimeFlags.default)
}
Service logic
The service method receives the @RequestBody and the Spring-injected MessageRepository. It wires the repository into the ZIO environment and returns ZIO[Any, Throwable, Message]. OfficeFloor (via officescala_zio) detects the ZIO return type, runs it, and passes the result as the @Parameter to the send method:
def service(@RequestBody request: ZioRequest, repository: MessageRepository): ZIO[Any, Throwable, Message] = {
val zio = for {
m <- MessageService.getMessage(request.id)
} yield m
zio.provide(ZLayer.succeed(repository))
}
def send(@Parameter message: Message, response: ObjectResponse[ZioResponse]): Unit =
response.send(ZioResponse(message.getContent))
REST endpoint
service:
class: net.officefloor.tutorial.ziohttpserver.ServiceLogic
method: service
next: send
send:
class: net.officefloor.tutorial.ziohttpserver.ServiceLogic
method: send
Repository
The repository is a standard Spring Data JPA interface injected by Spring Boot:
@Repository
public interface MessageRepository extends CrudRepository<Message, Integer> {
}
@Entity
public class Message {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String content;
public Message() {
}
public Message(Integer id, String content) {
this.id = id;
this.content = content;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getContent() {
return this.content;
}
public void setContent(String content) {
this.content = content;
}
}
Request and Response
case class ZioRequest(id: Int)
case class ZioResponse(message: String)
Database
Spring Boot auto-initialises the embedded H2 database from data.sql on the classpath (Hibernate creates the schema from the JPA entity):
INSERT INTO message (id, content) VALUES (1, 'Hi via ZIO');
INSERT INTO message (id, content) VALUES (2, 'Hello World');
INSERT INTO message (id, content) VALUES (3, 'I can do ZIO');
Testing
@SpringBootTest
@AutoConfigureMockMvc
class ZioHttpServerTest {
@Autowired var mvc: MockMvc = _
@Autowired var mapper: ObjectMapper = _
@Test
def getMessage(): Unit = {
mvc.perform(post("/")
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(ZioRequest(1)))
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk)
.andExpect(content().json(mapper.writeValueAsString(ZioResponse("Hi via ZIO"))))
}
}
Next
Return to the tutorials index to explore the full range of OfficeFloor capabilities.

