SlideShare a Scribd company logo
Functional, Type-safe,
Testable Microservices
with ZIO and gRPC
Nadav Samet
https://www.linkedin.com/in/nadav-samet/
July 16, 2020
Agenda
Microservices
gRPC and why not JSON
gRPC in Java and Scala
Why use ZIO for gRPC
ZIO and gRPC In Action
Live demo!
1
2
3
Microservice Architecture
$
</>
Why Microservices?
Deployed Independently
Faster deployments and
increased team autonomy.
Technology Heterogeneity
Use the right technology
for each job.
Scaling and Resilience
Failures can be isolated.
Scale only what needs to
be scaled.
Microservices: Types of Protocols
Ad-hoc
● JSON over HTTP
● Binary over TCP
Typically with no
machine-readable schema.
Message-based
● gRPC
● Thrift
● Twirp
Formal API contract.
Machine-readable schema.
Clients automatically
generated.
Data-oriented
● GraphQL
Designed for querying and
mutating data.
Clients automatically
generated.
Used as outer layer, not
between internal layers.
JSON over HTTP (REST): Pros and Cons
● No machine-readable API contract
● Custom libraries
● No advanced features
○ streaming, cancellations, retries,
timeouts
● Slow performance
● Ubiquitous
● Easy to understand
● Great tooling
What is gRPC?
● High-performance RPC framework
● Based on Protocol Buffers
● Type-safe API schema
● Supports many languages
● Runs on many platforms
● Allows schema evolution
● Advanced features:
○ Streaming
○ Cancellations
○ Deadlines
gRPC libraries for Scala and Java
● grpc-java
● ScalaPB gRPC
● Akka gRPC
● fs2-grpc
● zio-grpc
Why a new gRPC solution for ZIO?
ZIO gRPC builds on the unique strengths of ZIO:
✓ High performance
✓ RPC calls are functional effects (pure values: composable and combinable)
✓ Easy request cancellations via fiber interrupts
✓ Resource release guaranteed
✓ Precise failure tracking with no exceptions
✓ Combinable effectful streams with ZStream
✓ Dependency injection with ZLayer
Teaser on RPC cancellations
if (Context.current().isCancelled()) {
responseObserver.onError(
Status.CANCELLED.withDescription("Cancelled")
.asRuntimeException());
return;
}
In grpc-java:
Connection db = Database.connect();
try {
// do things with DB
} finally {
db.disconnect();
}
ZIO.bracket(Database.connect)(_.disconnect()) {
db =>
// do things with db
}
In ZIO gRPC:
Teaser on RPC cancellations
if (Context.current().isCancelled()) {
responseObserver.onError(
Status.CANCELLED.withDescription("Cancelled")
.asRuntimeException());
return;
}
In grpc-java:
Connection db = Database.connect();
try {
// do things with DB
} finally {
db.disconnect();
}
ZIO.bracket(Database.connect)(_.disconnect()) {
db =>
(effect1 *> effect2).uninterruptible *>
effect3
}
In ZIO gRPC:
AnyHike
A purely functional type-safe
hike logging service with ZIO
and gRPC.
Location message
syntax = "proto3";
message Location {
double lat = 1;
double lng = 2;
int64 timestamp = 3;
}
final case class Location(
lat: Double, lng: Double, timestamp: Long) {
def toByteArray: Array[Byte] = { … }
}
object Location {
def parseFrom(bs: Array[Byte]): Location = { … }
}
val l = Location(lat=37.3895, lng= -118.7588, timestamp=System.currentTimeMillis())
l.toByteArray.toVector
// Vector(9, 96, -27, -48, 34, -37, -79, 66, 64, 17, 27, 13, -32, 45, -112, ...)
Location.parseFrom(Array[Byte](9, 96, -27, ...))
// Location(37.3895,-118.7588,1594239025145)
AnyHike: Service Definition
service HikeStore {
rpc AddLocations (AddLocationsRequest ) returns (AddLocationsResponse );
rpc GetLocations (GetLocationsRequest ) returns (GetLocationsResponse );
rpc StreamLocations (GetLocationsRequest ) returns (stream Location );
}
message Location {
double lat = 1;
double lng = 2;
int64 timestamp = 3;
}
message AddLocationsRequest {
repeated Location locations = 1;
}
message AddLocationsResponse {
int32 size = 1; // current number of locations
}
message GetLocationsRequest { }
message GetLocationsResponse {
repeated Location locations = 1;
}
AnyHike: Service Definition
service HikeStore {
rpc AddLocations (AddLocationsRequest ) returns (AddLocationsResponse );
rpc GetLocations (GetLocationsRequest ) returns (GetLocationsResponse );
rpc StreamLocations (GetLocationsRequest ) returns (stream Location );
}
message Location {
double lat = 1;
double lng = 2;
int64 timestamp = 3;
}
message AddLocationsRequest {
repeated Location locations = 1;
}
message AddLocationsResponse {
int32 size = 1; // current number of locations
}
message GetLocationsRequest { }
message GetLocationsResponse {
repeated Location locations = 1;
}
AnyHike: Service Definition
trait HikeStore {
def addLocations(req: AddLocationsRequest): IO[Status, AddLocationsResponse]
def getLocations(req: GetLocationsRequest): IO[Status, GetLocationsResponse]
def streamLocations(req: GetLocationsRequest): zio.Stream[Status, Location]
}
service HikeStore {
rpc AddLocations (AddLocationsRequest ) returns (AddLocationsResponse );
rpc GetLocations (GetLocationsRequest ) returns (GetLocationsResponse );
rpc StreamLocations (GetLocationsRequest ) returns (stream Location);
}
IO[E, A]: effect that may succeed with a value of type A or fail with a value of type E.
Stream[E, A]: effectful stream that produces values of type A and can potentially fail with
a value of type E.
HikeStore: Client interpretation
trait HikeStore {
def addLocations(req: AddLocationsRequest): IO[Status, AddLocationsResponse]
def getLocations(req: GetLocationsRequest): IO[Status, GetLocationsResponse]
def streamLocations(req: GetLocationsRequest): zio.Stream[Status, Location]
}
val client: HikeStore = connect() // connect to service
client.addLocations(AddLocationsRequest(Seq(Location(12.34, 56.78))))
HikeStore: Server interpretation
object HikeStoreServer extends HikeStore {
def addLocations(request: AddLocationsRequest) = IO.succeed(AddLocationsResponse(17))
// more methods
}
AnyHike: Environment and Context
trait ZHikeStore[R, Context] {
def addLocations(req: AddLocationsRequest): ZIO[R with Context, Status, AddLocationsResponse]
def getLocations(req: GetLocationsRequest): ZIO[R with Context, Status, GetLocationsResponse]
def streamLocations(req: GetLocationsRequest): ZStream[R with Context, Status, Location]
}
type HikeStore = ZHikeStore[Any, Any]
Dependencies Headers (Metadata)
AnyHike: Service Definition
case class User(name: String)
object HikeStoreImpl extends ZHikeStore[Any, Has[User]] {
def addLocations(request: AddLocationsRequest): ZIO[Has[User],Status,AddLocationsResponse] =
for {
user <- ZIO.service[User]
p <- doSomething(user.name)
} yield AddLocationsResponse(p.result)
// ...
def authenticate(rc: RequestContext): IO[Status, User] =
rc.metadata.get(UserKey).flatMap {
case Some("bob") => IO.fail(Status.PERMISSION_DENIED.withDescription("You are not allowed!"))
case Some(name) => IO.succeed(User(name))
case None => IO.fail(Status.UNAUTHENTICATED)
}
val hikeStore: ZHikeStore[Any, Has[RequestContext]] = HikeStoreImpl.transformContextM(authenticate)
Dependencies Context
Service transformations
ZHikeStore[Any, Has[User]] which expands to {
def addLocations(req: AddLocationsRequest):
ZIO[Has[User], Status, AddLocationsResponse]
def getLocations(req: GetLocationsRequest):
ZIO[Has[User], Status, GetLocationsResponse]
def streamLocations(req: GetLocationsRequest):
ZStream[Has[User], Status, Location]
}
Dependencies Context
ZHikeStore[Any, Has[RequestContext]] which expands to {
def addLocations(req: AddLocationsRequest):
ZIO[Has[RequestContext], Status, AddLocationsResponse]
def getLocations(req: GetLocationsRequest):
ZIO[Has[RequestContext], Status, GetLocationsResponse]
def streamLocations(req: GetLocationsRequest):
ZStream[Has[RequestContext], Status, Location]
}
Applies the same effectful transformation to all RPC methods:
● Authentication and Authorization
● Logging and tracing
● Global policies (timeouts for all methods)
Demo!
Conclusion
It’s easy to get started. Create a client and a service in minutes
and build your next API on a modern effect system!
Links:
● ZIO gRPC: https://github.com/scalapb/zio-grpc/
● ScalaPB: https://scalapb.github.io/
● Gitter: https://gitter.im/ScalaPB/community

More Related Content

PDF
One Year of Clean Architecture - The Good, The Bad and The Bob
PPTX
Zero-Overhead Metaprogramming: Reflection and Metaobject Protocols Fast and w...
PPTX
Gor Nishanov, C++ Coroutines – a negative overhead abstraction
PDF
04 - Qt Data
PPTX
Building High-Performance Language Implementations With Low Effort
PPT
Using QString effectively
PDF
Virtual machine and javascript engine
PDF
Start Wrap Episode 11: A New Rope
One Year of Clean Architecture - The Good, The Bad and The Bob
Zero-Overhead Metaprogramming: Reflection and Metaobject Protocols Fast and w...
Gor Nishanov, C++ Coroutines – a negative overhead abstraction
04 - Qt Data
Building High-Performance Language Implementations With Low Effort
Using QString effectively
Virtual machine and javascript engine
Start Wrap Episode 11: A New Rope

What's hot (20)

PPTX
Optimizing Communicating Event-Loop Languages with Truffle
PPTX
Return of c++
PDF
Go and Uber’s time series database m3
PPTX
Async await in C++
PDF
[JavaOne 2011] Models for Concurrent Programming
DOC
Qtp realtime scripts
PDF
ConFess Vienna 2015 - Metaprogramming with Groovy
PDF
Cascadia.js: Don't Cross the Streams
PDF
Exploiting Concurrency with Dynamic Languages
PPTX
Performance in .net best practices
PPTX
Beginning direct3d gameprogrammingcpp02_20160324_jintaeks
PPT
20100712-OTcl Command -- Getting Started
PDF
Blocks & GCD
PDF
서버 개발자가 바라 본 Functional Reactive Programming with RxJava - SpringCamp2015
PPTX
Seeing with Python presented at PyCon AU 2014
PPT
NS2 Classifiers
PDF
06 - Qt Communication
PDF
C++ How I learned to stop worrying and love metaprogramming
DOC
Qtp+real time+test+script
PPTX
C++ Coroutines
Optimizing Communicating Event-Loop Languages with Truffle
Return of c++
Go and Uber’s time series database m3
Async await in C++
[JavaOne 2011] Models for Concurrent Programming
Qtp realtime scripts
ConFess Vienna 2015 - Metaprogramming with Groovy
Cascadia.js: Don't Cross the Streams
Exploiting Concurrency with Dynamic Languages
Performance in .net best practices
Beginning direct3d gameprogrammingcpp02_20160324_jintaeks
20100712-OTcl Command -- Getting Started
Blocks & GCD
서버 개발자가 바라 본 Functional Reactive Programming with RxJava - SpringCamp2015
Seeing with Python presented at PyCon AU 2014
NS2 Classifiers
06 - Qt Communication
C++ How I learned to stop worrying and love metaprogramming
Qtp+real time+test+script
C++ Coroutines
Ad

Similar to Functional, Type-safe, Testable Microservices with ZIO and gRPC (20)

PDF
Ray tracing with ZIO-ZLayer
PDF
RESTful API using scalaz (3)
PDF
Microservices in GO - Massimiliano Dessì - Codemotion Rome 2017
PDF
Bringing Learnings from Googley Microservices with gRPC - Varun Talwar, Google
PDF
Async Microservices with Twitter's Finagle
PDF
"Enabling Googley microservices with gRPC" at JEEConf 2017
PDF
"Enabling Googley microservices with gRPC" at JDK.IO 2017
PDF
Generating Unified APIs with Protocol Buffers and gRPC
PDF
Mасштабирование микросервисов на Go, Matt Heath (Hailo)
PDF
Fast and Reliable Swift APIs with gRPC
PPTX
Yotpo microservices
PDF
ZLayer
PDF
GDG Cloud Taipei meetup #50 - Build go kit microservices at kubernetes with ...
PPTX
Chapter 05: Eclipse Vert.x - Service Discovery, Resilience and Stability Patt...
PDF
Composition of Semantic Geo Services
PDF
"Enabling Googley microservices with gRPC." at Devoxx France 2017
PPTX
CocoaConf: The Language of Mobile Software is APIs
PDF
Julio Capote, Twitter
PDF
HBaseCon 2012 | HBase Security for the Enterprise - Andrew Purtell, Trend Micro
PDF
Break me if you can: practical guide to building fault-tolerant systems (with...
Ray tracing with ZIO-ZLayer
RESTful API using scalaz (3)
Microservices in GO - Massimiliano Dessì - Codemotion Rome 2017
Bringing Learnings from Googley Microservices with gRPC - Varun Talwar, Google
Async Microservices with Twitter's Finagle
"Enabling Googley microservices with gRPC" at JEEConf 2017
"Enabling Googley microservices with gRPC" at JDK.IO 2017
Generating Unified APIs with Protocol Buffers and gRPC
Mасштабирование микросервисов на Go, Matt Heath (Hailo)
Fast and Reliable Swift APIs with gRPC
Yotpo microservices
ZLayer
GDG Cloud Taipei meetup #50 - Build go kit microservices at kubernetes with ...
Chapter 05: Eclipse Vert.x - Service Discovery, Resilience and Stability Patt...
Composition of Semantic Geo Services
"Enabling Googley microservices with gRPC." at Devoxx France 2017
CocoaConf: The Language of Mobile Software is APIs
Julio Capote, Twitter
HBaseCon 2012 | HBase Security for the Enterprise - Andrew Purtell, Trend Micro
Break me if you can: practical guide to building fault-tolerant systems (with...
Ad

Recently uploaded (20)

PDF
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
PDF
Design an Analysis of Algorithms I-SECS-1021-03
PDF
How Creative Agencies Leverage Project Management Software.pdf
PPTX
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
PDF
System and Network Administraation Chapter 3
PPTX
Operating system designcfffgfgggggggvggggggggg
PDF
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
PPTX
CHAPTER 2 - PM Management and IT Context
PDF
Digital Strategies for Manufacturing Companies
PDF
Adobe Illustrator 28.6 Crack My Vision of Vector Design
PDF
wealthsignaloriginal-com-DS-text-... (1).pdf
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 41
PPTX
L1 - Introduction to python Backend.pptx
PDF
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
PDF
Design an Analysis of Algorithms II-SECS-1021-03
PDF
Understanding Forklifts - TECH EHS Solution
PPTX
Transform Your Business with a Software ERP System
PDF
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...
PPTX
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
PDF
Odoo Companies in India – Driving Business Transformation.pdf
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
Design an Analysis of Algorithms I-SECS-1021-03
How Creative Agencies Leverage Project Management Software.pdf
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
System and Network Administraation Chapter 3
Operating system designcfffgfgggggggvggggggggg
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
CHAPTER 2 - PM Management and IT Context
Digital Strategies for Manufacturing Companies
Adobe Illustrator 28.6 Crack My Vision of Vector Design
wealthsignaloriginal-com-DS-text-... (1).pdf
Internet Downloader Manager (IDM) Crack 6.42 Build 41
L1 - Introduction to python Backend.pptx
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
Design an Analysis of Algorithms II-SECS-1021-03
Understanding Forklifts - TECH EHS Solution
Transform Your Business with a Software ERP System
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
Odoo Companies in India – Driving Business Transformation.pdf

Functional, Type-safe, Testable Microservices with ZIO and gRPC

  • 1. Functional, Type-safe, Testable Microservices with ZIO and gRPC Nadav Samet https://www.linkedin.com/in/nadav-samet/ July 16, 2020
  • 2. Agenda Microservices gRPC and why not JSON gRPC in Java and Scala Why use ZIO for gRPC ZIO and gRPC In Action Live demo! 1 2 3
  • 4. Why Microservices? Deployed Independently Faster deployments and increased team autonomy. Technology Heterogeneity Use the right technology for each job. Scaling and Resilience Failures can be isolated. Scale only what needs to be scaled.
  • 5. Microservices: Types of Protocols Ad-hoc ● JSON over HTTP ● Binary over TCP Typically with no machine-readable schema. Message-based ● gRPC ● Thrift ● Twirp Formal API contract. Machine-readable schema. Clients automatically generated. Data-oriented ● GraphQL Designed for querying and mutating data. Clients automatically generated. Used as outer layer, not between internal layers.
  • 6. JSON over HTTP (REST): Pros and Cons ● No machine-readable API contract ● Custom libraries ● No advanced features ○ streaming, cancellations, retries, timeouts ● Slow performance ● Ubiquitous ● Easy to understand ● Great tooling
  • 7. What is gRPC? ● High-performance RPC framework ● Based on Protocol Buffers ● Type-safe API schema ● Supports many languages ● Runs on many platforms ● Allows schema evolution ● Advanced features: ○ Streaming ○ Cancellations ○ Deadlines
  • 8. gRPC libraries for Scala and Java ● grpc-java ● ScalaPB gRPC ● Akka gRPC ● fs2-grpc ● zio-grpc
  • 9. Why a new gRPC solution for ZIO? ZIO gRPC builds on the unique strengths of ZIO: ✓ High performance ✓ RPC calls are functional effects (pure values: composable and combinable) ✓ Easy request cancellations via fiber interrupts ✓ Resource release guaranteed ✓ Precise failure tracking with no exceptions ✓ Combinable effectful streams with ZStream ✓ Dependency injection with ZLayer
  • 10. Teaser on RPC cancellations if (Context.current().isCancelled()) { responseObserver.onError( Status.CANCELLED.withDescription("Cancelled") .asRuntimeException()); return; } In grpc-java: Connection db = Database.connect(); try { // do things with DB } finally { db.disconnect(); } ZIO.bracket(Database.connect)(_.disconnect()) { db => // do things with db } In ZIO gRPC:
  • 11. Teaser on RPC cancellations if (Context.current().isCancelled()) { responseObserver.onError( Status.CANCELLED.withDescription("Cancelled") .asRuntimeException()); return; } In grpc-java: Connection db = Database.connect(); try { // do things with DB } finally { db.disconnect(); } ZIO.bracket(Database.connect)(_.disconnect()) { db => (effect1 *> effect2).uninterruptible *> effect3 } In ZIO gRPC:
  • 12. AnyHike A purely functional type-safe hike logging service with ZIO and gRPC.
  • 13. Location message syntax = "proto3"; message Location { double lat = 1; double lng = 2; int64 timestamp = 3; } final case class Location( lat: Double, lng: Double, timestamp: Long) { def toByteArray: Array[Byte] = { … } } object Location { def parseFrom(bs: Array[Byte]): Location = { … } } val l = Location(lat=37.3895, lng= -118.7588, timestamp=System.currentTimeMillis()) l.toByteArray.toVector // Vector(9, 96, -27, -48, 34, -37, -79, 66, 64, 17, 27, 13, -32, 45, -112, ...) Location.parseFrom(Array[Byte](9, 96, -27, ...)) // Location(37.3895,-118.7588,1594239025145)
  • 14. AnyHike: Service Definition service HikeStore { rpc AddLocations (AddLocationsRequest ) returns (AddLocationsResponse ); rpc GetLocations (GetLocationsRequest ) returns (GetLocationsResponse ); rpc StreamLocations (GetLocationsRequest ) returns (stream Location ); } message Location { double lat = 1; double lng = 2; int64 timestamp = 3; } message AddLocationsRequest { repeated Location locations = 1; } message AddLocationsResponse { int32 size = 1; // current number of locations } message GetLocationsRequest { } message GetLocationsResponse { repeated Location locations = 1; }
  • 15. AnyHike: Service Definition service HikeStore { rpc AddLocations (AddLocationsRequest ) returns (AddLocationsResponse ); rpc GetLocations (GetLocationsRequest ) returns (GetLocationsResponse ); rpc StreamLocations (GetLocationsRequest ) returns (stream Location ); } message Location { double lat = 1; double lng = 2; int64 timestamp = 3; } message AddLocationsRequest { repeated Location locations = 1; } message AddLocationsResponse { int32 size = 1; // current number of locations } message GetLocationsRequest { } message GetLocationsResponse { repeated Location locations = 1; }
  • 16. AnyHike: Service Definition trait HikeStore { def addLocations(req: AddLocationsRequest): IO[Status, AddLocationsResponse] def getLocations(req: GetLocationsRequest): IO[Status, GetLocationsResponse] def streamLocations(req: GetLocationsRequest): zio.Stream[Status, Location] } service HikeStore { rpc AddLocations (AddLocationsRequest ) returns (AddLocationsResponse ); rpc GetLocations (GetLocationsRequest ) returns (GetLocationsResponse ); rpc StreamLocations (GetLocationsRequest ) returns (stream Location); } IO[E, A]: effect that may succeed with a value of type A or fail with a value of type E. Stream[E, A]: effectful stream that produces values of type A and can potentially fail with a value of type E.
  • 17. HikeStore: Client interpretation trait HikeStore { def addLocations(req: AddLocationsRequest): IO[Status, AddLocationsResponse] def getLocations(req: GetLocationsRequest): IO[Status, GetLocationsResponse] def streamLocations(req: GetLocationsRequest): zio.Stream[Status, Location] } val client: HikeStore = connect() // connect to service client.addLocations(AddLocationsRequest(Seq(Location(12.34, 56.78))))
  • 18. HikeStore: Server interpretation object HikeStoreServer extends HikeStore { def addLocations(request: AddLocationsRequest) = IO.succeed(AddLocationsResponse(17)) // more methods }
  • 19. AnyHike: Environment and Context trait ZHikeStore[R, Context] { def addLocations(req: AddLocationsRequest): ZIO[R with Context, Status, AddLocationsResponse] def getLocations(req: GetLocationsRequest): ZIO[R with Context, Status, GetLocationsResponse] def streamLocations(req: GetLocationsRequest): ZStream[R with Context, Status, Location] } type HikeStore = ZHikeStore[Any, Any] Dependencies Headers (Metadata)
  • 20. AnyHike: Service Definition case class User(name: String) object HikeStoreImpl extends ZHikeStore[Any, Has[User]] { def addLocations(request: AddLocationsRequest): ZIO[Has[User],Status,AddLocationsResponse] = for { user <- ZIO.service[User] p <- doSomething(user.name) } yield AddLocationsResponse(p.result) // ... def authenticate(rc: RequestContext): IO[Status, User] = rc.metadata.get(UserKey).flatMap { case Some("bob") => IO.fail(Status.PERMISSION_DENIED.withDescription("You are not allowed!")) case Some(name) => IO.succeed(User(name)) case None => IO.fail(Status.UNAUTHENTICATED) } val hikeStore: ZHikeStore[Any, Has[RequestContext]] = HikeStoreImpl.transformContextM(authenticate) Dependencies Context
  • 21. Service transformations ZHikeStore[Any, Has[User]] which expands to { def addLocations(req: AddLocationsRequest): ZIO[Has[User], Status, AddLocationsResponse] def getLocations(req: GetLocationsRequest): ZIO[Has[User], Status, GetLocationsResponse] def streamLocations(req: GetLocationsRequest): ZStream[Has[User], Status, Location] } Dependencies Context ZHikeStore[Any, Has[RequestContext]] which expands to { def addLocations(req: AddLocationsRequest): ZIO[Has[RequestContext], Status, AddLocationsResponse] def getLocations(req: GetLocationsRequest): ZIO[Has[RequestContext], Status, GetLocationsResponse] def streamLocations(req: GetLocationsRequest): ZStream[Has[RequestContext], Status, Location] } Applies the same effectful transformation to all RPC methods: ● Authentication and Authorization ● Logging and tracing ● Global policies (timeouts for all methods)
  • 22. Demo!
  • 23. Conclusion It’s easy to get started. Create a client and a service in minutes and build your next API on a modern effect system! Links: ● ZIO gRPC: https://github.com/scalapb/zio-grpc/ ● ScalaPB: https://scalapb.github.io/ ● Gitter: https://gitter.im/ScalaPB/community