SlideShare a Scribd company logo
DESIGNING A
FUNCTIONAL GRAPHQL LIBRARY
Functional Scala 2019
WHO AM I?
> Pierre Ricadat aka @ghostdogpr
>
!
exiled in
"
> Contributor to ZIO
> Creator of Caliban
GRAPHQL ?
GRAPHQL IN A NUTSHELL
> query language for APIs
> server exposes a typed schema
> client asks for what they want
> queries
> mutations
> subscriptions
WHY?
WHY CREATE A GRAPHQL LIBRARY?
> Sangria is great but...
> lots of boilerplate
> Future-based
> schema and resolver tied together
> interesting approach by Morpheus (Haskell)
> it's fun!
GOALS
> minimal boilerplate
> purely functional
> strongly typed
> explicit errors
> ZIO
> schema / resolver separation
PLAN OF ACTION
query → parsing → validation → execution → result
↑
schema
QUERY PARSING
query → parsing → validation → execution → result
↑
schema
QUERY PARSING
GraphQL spec: https://graphql.github.io/graphql-spec/
QUERY PARSING
Fastparse: fast, easy to use, good documentation
def name[_: P]: P[String] = P(CharIn("_A-Za-z") ~~ CharIn("_0-9A-Za-z").repX).!
def fragmentName[_: P]: P[String] = P(name).filter(_ != "on")
def fragmentSpread[_: P]: P[FragmentSpread] =
P("..." ~ fragmentName ~ directives).map {
case (name, dirs) => FragmentSpread(name, dirs)
}
SCHEMA
query → parsing → validation → execution → result
↑
schema
SCHEMA
Idea from Morpheus:
derive GraphQL schema from basic types
SIMPLE API
type Queries {
pug: Pug!
}
case class Queries(pug: Pug)
OBJECTS
type Pug {
name: String!
nicknames: [String!]!
favoriteFood: String
}
case class Pug(
name: String,
nicknames: List[String],
favoriteFood: Option[String])
ENUMS
enum Color {
FAWN
BLACK
OTHER
}
sealed trait Color
case object FAWN extends Color
case object BLACK extends Color
case object OTHER extends Color
ARGUMENTS
type Queries {
pug(name: String!): Pug
}
case class PugName(name: String)
case class Queries(pug: PugName => Option[Pug])
EFFECTS
type Queries {
pug(name: String!): Pug
}
case class PugName(name: String)
case class Queries(pug: PugName => Task[Pug])
RESOLVER
simple value
case class PugName(name: String)
case class Queries(
pug: PugName => Task[Pug]
pugs: Task[List[Pug]]
)
val resolver = Queries(
args => PugService.findPug(args.name),
PugService.getAllPugs
)
SCHEMA
trait Schema[-R, T] {
// describe the type T
def toType(isInput: Boolean = false): __Type
// describe how to resolve a value of type T
def resolve(value: T): Step[R]
}
WHAT'S A STEP
> Pure Value (leaf)
> List
> Object
> Effect
> Stream
SCHEMA FOR STRING
implicit val stringSchema = new Schema[Any, String] {
def toType(isInput: Boolean = false): __Type =
__Type(__TypeKind.SCALAR, Some("String"))
def resolve(value: String): Step[R] =
PureStep(StringValue(value))
}
Schema for case classes and sealed traits?
MAGNOLIA
>
!
> ease of use
> compile time
> documentation, examples
>
"
> error messages (getting better!)
MAGNOLIA DERIVATION
def combine[T](
caseClass: CaseClass[Typeclass, T]): Typeclass[T]
def dispatch[T](
sealedTrait: SealedTrait[Typeclass, T]): Typeclass[T]
VALIDATION
query → parsing → validation → execution → result
↑
schema
VALIDATION
Back to the specs
EXECUTION
query → parsing → validation → execution → result
↑
schema
EXECUTION
1. Schema + Resolver => Execution Plan (tree of Steps)
2. Filter Plan to include only requested fields
3. Reduce Plan to pure values and effects
4. Run effects
N+1 PROBLEM
{
orders { # fetches orders (1 query)
id
customer { # fetches customer (n queries)
name
}
}
}
QUERY OPTIMIZATION
Naive ZIO[R, E, A] version
val getAllUserIds: ZIO[Any, Nothing, List[Int]] = ???
def getUserNameById(id: Int): ZIO[Any, Nothing, String] = ???
for {
userIds <- getAllUserIds
userNames <- ZIO.foreachPar(userIds)(getUserNameById)
} yield userNames
QUERY OPTIMIZATION
Meet ZQuery[R, E, A]
val getAllUserIds: ZQuery[Any, Nothing, List[Int]] = ???
def getUserNameById(id: Int): ZQuery[Any, Nothing, String] = ???
for {
userIds <- getAllUserIds
userNames <- ZQuery.foreachPar(userIds)(getUserNameById)
} yield userNames
ZQUERY BENEFITS
> parallelize queries
> cache identical queries (deduplication)
> batch queries if batching function provided
ZQUERY
> courtesy of @adamgfraser
> based on paper on Haxl
> "There is no Fork: An Abstraction for Efficient,
Concurrent, and Concise Data Access"
> will be extracted to its own library at some point
INTROSPECTION
Dogfooding
case class __Introspection(
__schema: __Schema,
__type: __TypeArgs => __Type)
USAGE
1. Define your queries / mutations / subscriptions
2. Provide a Schema for custom types
3. Provide a resolver
4. Profit
1. DEFINE YOUR QUERIES
case class Pug(name: String, nicknames: List[String], favoriteFood: Option[String])
case class PugName(name: NonEmptyString)
case class Queries(pugs: Task[List[Pug]], pug: PugName => Task[Pug])
type Pug {
name: String!
nicknames: [String!]!
favoriteFood: String
}
type Queries {
pugs: [Pug!]
pug(name: String!): Pug
}
2. PROVIDE A SCHEMA FOR CUSTOM TYPES
implicit val nesSchema: Schema[NonEmptyString] =
Schema.stringSchema.contramap(_.value)
3. PROVIDE A RESOLVER
def getPugs: Task[List[Pug]] = ???
def getPug(name: String): Task[Pug] = ???
val queries = Queries(
getPugs,
args => getPug(args.name))
val resolver = RootResolver(queries)
4. PROFIT
val interpreter = graphQL(resolver)
for {
result <- interpreter.execute(query)
_ <- zio.console.putStrLn(result.toString)
} yield ()
HTTP4S MODULE
val route: HttpRoutes[RIO[R, *]] =
Http4sAdapter.makeRestService(interpreter)
val wsRoute: HttpRoutes[RIO[R, *]] =
Http4sAdapter.makeWebSocketService(interpreter)
SCALAJS
Few and modern dependencies
ScalaJS support for free
CATS COMPATIBILITY
caliban-cats module
def executeAsync[F[_]: Async](...)(
implicit runtime: Runtime[Any]): F[GraphQLResponse[E]]
def makeRestServiceF[F[_]: Effect, Q, M, S, E](...)(
implicit runtime: Runtime[Any]): HttpRoutes[F]
PERFORMANCE
Caliban is fast
Check benchmarks module on github
FUTURE
> Query analysis
> Middleware (query-level, field-level)
> Code generation
> Other HTTP adapters (Akka HTTP) <- help needed
> Client support
WANNA KNOW MORE?
> Website: https://ghostdogpr.github.io/caliban/
> ZIO Discord: #caliban
THANKS!QUESTIONS?

More Related Content

PDF
Annotation processing and code gen
koji lin
 
PDF
Ramda, a functional JavaScript library
Derek Willian Stavis
 
PDF
Managing GraphQL servers with AWS Fargate & Prisma Cloud
Nikolas Burk
 
PDF
Ramda lets write declarative js
Pivorak MeetUp
 
PDF
Programming with Python and PostgreSQL
Peter Eisentraut
 
PDF
#ajn3.lt.marblejenka
Shingo Furuyama
 
PDF
Lambdas and Streams Master Class Part 2
José Paumard
 
PDF
Stubる - Mockingjayを使ったHTTPクライアントのテスト -
Kenji Tanaka
 
Annotation processing and code gen
koji lin
 
Ramda, a functional JavaScript library
Derek Willian Stavis
 
Managing GraphQL servers with AWS Fargate & Prisma Cloud
Nikolas Burk
 
Ramda lets write declarative js
Pivorak MeetUp
 
Programming with Python and PostgreSQL
Peter Eisentraut
 
#ajn3.lt.marblejenka
Shingo Furuyama
 
Lambdas and Streams Master Class Part 2
José Paumard
 
Stubる - Mockingjayを使ったHTTPクライアントのテスト -
Kenji Tanaka
 

What's hot (20)

PPT
Testing Javascript with Jasmine
Tim Tyrrell
 
PDF
Xlab #1: Advantages of functional programming in Java 8
XSolve
 
KEY
JavaScript Classes and Inheritance
marcheiligers
 
PDF
JDK8 : parallel programming made (too ?) easy
José Paumard
 
PDF
PHP 8.1 - What's new and changed
Ayesh Karunaratne
 
PDF
Object Oriented JavaScript
Michael Girouard
 
PPTX
What’s new in C# 6
Fiyaz Hasan
 
PDF
Scala introduction
vito jeng
 
ODP
JavaScript Web Development
vito jeng
 
PPTX
Java script advance-auroskills (2)
BoneyGawande
 
PDF
Scala @ TechMeetup Edinburgh
Stuart Roebuck
 
PDF
Java 8 Stream API (Valdas Zigas)
Kaunas Java User Group
 
PPTX
Arrays &amp; functions in php
Ashish Chamoli
 
PDF
Test-Driven Development of AngularJS Applications
FITC
 
PDF
ReactでGraphQLを使っている
Takahiro Kobaru
 
PDF
Advanced Object-Oriented JavaScript
ecker
 
PDF
Being functional in PHP (DPC 2016)
David de Boer
 
PPTX
Expression trees in c#, Алексей Голубь (Svitla Systems)
Alina Vilk
 
PDF
Building High Performance and Reliable Windows Phone 8 Apps
Michele Capra
 
PDF
Jggug 2010 330 Grails 1.3 観察
Tsuyoshi Yamamoto
 
Testing Javascript with Jasmine
Tim Tyrrell
 
Xlab #1: Advantages of functional programming in Java 8
XSolve
 
JavaScript Classes and Inheritance
marcheiligers
 
JDK8 : parallel programming made (too ?) easy
José Paumard
 
PHP 8.1 - What's new and changed
Ayesh Karunaratne
 
Object Oriented JavaScript
Michael Girouard
 
What’s new in C# 6
Fiyaz Hasan
 
Scala introduction
vito jeng
 
JavaScript Web Development
vito jeng
 
Java script advance-auroskills (2)
BoneyGawande
 
Scala @ TechMeetup Edinburgh
Stuart Roebuck
 
Java 8 Stream API (Valdas Zigas)
Kaunas Java User Group
 
Arrays &amp; functions in php
Ashish Chamoli
 
Test-Driven Development of AngularJS Applications
FITC
 
ReactでGraphQLを使っている
Takahiro Kobaru
 
Advanced Object-Oriented JavaScript
ecker
 
Being functional in PHP (DPC 2016)
David de Boer
 
Expression trees in c#, Алексей Голубь (Svitla Systems)
Alina Vilk
 
Building High Performance and Reliable Windows Phone 8 Apps
Michele Capra
 
Jggug 2010 330 Grails 1.3 観察
Tsuyoshi Yamamoto
 
Ad

Similar to Designing a Functional GraphQL Library (20)

PDF
Java 8 Workshop
Mario Fusco
 
PDF
Thumbtack Expertise Days # 5 - Javaz
Alexey Remnev
 
PDF
FP in Java - Project Lambda and beyond
Mario Fusco
 
PDF
NLJUG University Sessie: Java Reborn, Powered by Ordina
Martijn Blankestijn
 
PDF
Currying and Partial Function Application (PFA)
Dhaval Dalal
 
PDF
Scala is java8.next()
daewon jeong
 
PPTX
What's new in Java 8
Kyle Smith
 
PDF
Swift, functional programming, and the future of Objective-C
Alexis Gallagher
 
PPTX
What is new in Java 8
Sandeep Kr. Singh
 
PDF
Metaprogramovanie #1
Jano Suchal
 
KEY
Static or Dynamic Typing? Why not both?
Mario Camou Riveroll
 
PDF
How to make the fastest Router in Python
kwatch
 
PDF
Functional Java 8 in everyday life
Andrea Iacono
 
PDF
Scala for Java Programmers
Eric Pederson
 
PPTX
Functional Programming in JavaScript by Luis Atencio
Luis Atencio
 
PPTX
LinkedIn TBC JavaScript 100: Functions
Adam Crabtree
 
PPTX
Lambdas and Laughs
Jim Bethancourt
 
PPTX
Javascripting.pptx
Vinod Srivastava
 
PDF
Querydsl fin jug - june 2012
Timo Westkämper
 
PDF
Javaz. Functional design in Java 8.
Vadim Dubs
 
Java 8 Workshop
Mario Fusco
 
Thumbtack Expertise Days # 5 - Javaz
Alexey Remnev
 
FP in Java - Project Lambda and beyond
Mario Fusco
 
NLJUG University Sessie: Java Reborn, Powered by Ordina
Martijn Blankestijn
 
Currying and Partial Function Application (PFA)
Dhaval Dalal
 
Scala is java8.next()
daewon jeong
 
What's new in Java 8
Kyle Smith
 
Swift, functional programming, and the future of Objective-C
Alexis Gallagher
 
What is new in Java 8
Sandeep Kr. Singh
 
Metaprogramovanie #1
Jano Suchal
 
Static or Dynamic Typing? Why not both?
Mario Camou Riveroll
 
How to make the fastest Router in Python
kwatch
 
Functional Java 8 in everyday life
Andrea Iacono
 
Scala for Java Programmers
Eric Pederson
 
Functional Programming in JavaScript by Luis Atencio
Luis Atencio
 
LinkedIn TBC JavaScript 100: Functions
Adam Crabtree
 
Lambdas and Laughs
Jim Bethancourt
 
Javascripting.pptx
Vinod Srivastava
 
Querydsl fin jug - june 2012
Timo Westkämper
 
Javaz. Functional design in Java 8.
Vadim Dubs
 
Ad

Recently uploaded (20)

PPTX
Presentation about Database and Database Administrator
abhishekchauhan86963
 
PDF
Immersive experiences: what Pharo users do!
ESUG
 
PPTX
Contractor Management Platform and Software Solution for Compliance
SHEQ Network Limited
 
PPTX
TRAVEL APIs | WHITE LABEL TRAVEL API | TOP TRAVEL APIs
philipnathen82
 
PDF
Using licensed Data Loss Prevention (DLP) as a strategic proactive data secur...
Q-Advise
 
PPTX
GALILEO CRS SYSTEM | GALILEO TRAVEL SOFTWARE
philipnathen82
 
PPT
Activate_Methodology_Summary presentatio
annapureddyn
 
PPTX
ASSIGNMENT_1[1][1][1][1][1] (1) variables.pptx
kr2589474
 
PPTX
Role Of Python In Programing Language.pptx
jaykoshti048
 
PDF
Protecting the Digital World Cyber Securit
dnthakkar16
 
PDF
Bandai Playdia The Book - David Glotz
BluePanther6
 
PDF
Balancing Resource Capacity and Workloads with OnePlan – Avoid Overloading Te...
OnePlan Solutions
 
PPTX
Presentation about variables and constant.pptx
safalsingh810
 
PDF
Generating Union types w/ Static Analysis
K. Matthew Dupree
 
PDF
Enhancing Healthcare RPM Platforms with Contextual AI Integration
Cadabra Studio
 
PDF
Summary Of Odoo 18.1 to 18.4 : The Way For Odoo 19
CandidRoot Solutions Private Limited
 
PPT
Why Reliable Server Maintenance Service in New York is Crucial for Your Business
Sam Vohra
 
PDF
Applitools Platform Pulse: What's New and What's Coming - July 2025
Applitools
 
PDF
ChatPharo: an Open Architecture for Understanding How to Talk Live to LLMs
ESUG
 
PPTX
ConcordeApp: Engineering Global Impact & Unlocking Billions in Event ROI with AI
chastechaste14
 
Presentation about Database and Database Administrator
abhishekchauhan86963
 
Immersive experiences: what Pharo users do!
ESUG
 
Contractor Management Platform and Software Solution for Compliance
SHEQ Network Limited
 
TRAVEL APIs | WHITE LABEL TRAVEL API | TOP TRAVEL APIs
philipnathen82
 
Using licensed Data Loss Prevention (DLP) as a strategic proactive data secur...
Q-Advise
 
GALILEO CRS SYSTEM | GALILEO TRAVEL SOFTWARE
philipnathen82
 
Activate_Methodology_Summary presentatio
annapureddyn
 
ASSIGNMENT_1[1][1][1][1][1] (1) variables.pptx
kr2589474
 
Role Of Python In Programing Language.pptx
jaykoshti048
 
Protecting the Digital World Cyber Securit
dnthakkar16
 
Bandai Playdia The Book - David Glotz
BluePanther6
 
Balancing Resource Capacity and Workloads with OnePlan – Avoid Overloading Te...
OnePlan Solutions
 
Presentation about variables and constant.pptx
safalsingh810
 
Generating Union types w/ Static Analysis
K. Matthew Dupree
 
Enhancing Healthcare RPM Platforms with Contextual AI Integration
Cadabra Studio
 
Summary Of Odoo 18.1 to 18.4 : The Way For Odoo 19
CandidRoot Solutions Private Limited
 
Why Reliable Server Maintenance Service in New York is Crucial for Your Business
Sam Vohra
 
Applitools Platform Pulse: What's New and What's Coming - July 2025
Applitools
 
ChatPharo: an Open Architecture for Understanding How to Talk Live to LLMs
ESUG
 
ConcordeApp: Engineering Global Impact & Unlocking Billions in Event ROI with AI
chastechaste14
 

Designing a Functional GraphQL Library

  • 1. DESIGNING A FUNCTIONAL GRAPHQL LIBRARY Functional Scala 2019
  • 2. WHO AM I? > Pierre Ricadat aka @ghostdogpr > ! exiled in " > Contributor to ZIO > Creator of Caliban
  • 4. GRAPHQL IN A NUTSHELL > query language for APIs > server exposes a typed schema > client asks for what they want > queries > mutations > subscriptions
  • 6. WHY CREATE A GRAPHQL LIBRARY? > Sangria is great but... > lots of boilerplate > Future-based > schema and resolver tied together > interesting approach by Morpheus (Haskell) > it's fun!
  • 7. GOALS > minimal boilerplate > purely functional > strongly typed > explicit errors > ZIO > schema / resolver separation
  • 8. PLAN OF ACTION query → parsing → validation → execution → result ↑ schema
  • 9. QUERY PARSING query → parsing → validation → execution → result ↑ schema
  • 10. QUERY PARSING GraphQL spec: https://graphql.github.io/graphql-spec/
  • 11. QUERY PARSING Fastparse: fast, easy to use, good documentation def name[_: P]: P[String] = P(CharIn("_A-Za-z") ~~ CharIn("_0-9A-Za-z").repX).! def fragmentName[_: P]: P[String] = P(name).filter(_ != "on") def fragmentSpread[_: P]: P[FragmentSpread] = P("..." ~ fragmentName ~ directives).map { case (name, dirs) => FragmentSpread(name, dirs) }
  • 12. SCHEMA query → parsing → validation → execution → result ↑ schema
  • 13. SCHEMA Idea from Morpheus: derive GraphQL schema from basic types
  • 14. SIMPLE API type Queries { pug: Pug! } case class Queries(pug: Pug)
  • 15. OBJECTS type Pug { name: String! nicknames: [String!]! favoriteFood: String } case class Pug( name: String, nicknames: List[String], favoriteFood: Option[String])
  • 16. ENUMS enum Color { FAWN BLACK OTHER } sealed trait Color case object FAWN extends Color case object BLACK extends Color case object OTHER extends Color
  • 17. ARGUMENTS type Queries { pug(name: String!): Pug } case class PugName(name: String) case class Queries(pug: PugName => Option[Pug])
  • 18. EFFECTS type Queries { pug(name: String!): Pug } case class PugName(name: String) case class Queries(pug: PugName => Task[Pug])
  • 19. RESOLVER simple value case class PugName(name: String) case class Queries( pug: PugName => Task[Pug] pugs: Task[List[Pug]] ) val resolver = Queries( args => PugService.findPug(args.name), PugService.getAllPugs )
  • 20. SCHEMA trait Schema[-R, T] { // describe the type T def toType(isInput: Boolean = false): __Type // describe how to resolve a value of type T def resolve(value: T): Step[R] }
  • 21. WHAT'S A STEP > Pure Value (leaf) > List > Object > Effect > Stream
  • 22. SCHEMA FOR STRING implicit val stringSchema = new Schema[Any, String] { def toType(isInput: Boolean = false): __Type = __Type(__TypeKind.SCALAR, Some("String")) def resolve(value: String): Step[R] = PureStep(StringValue(value)) }
  • 23. Schema for case classes and sealed traits?
  • 24. MAGNOLIA > ! > ease of use > compile time > documentation, examples > " > error messages (getting better!)
  • 25. MAGNOLIA DERIVATION def combine[T]( caseClass: CaseClass[Typeclass, T]): Typeclass[T] def dispatch[T]( sealedTrait: SealedTrait[Typeclass, T]): Typeclass[T]
  • 26. VALIDATION query → parsing → validation → execution → result ↑ schema
  • 28. EXECUTION query → parsing → validation → execution → result ↑ schema
  • 29. EXECUTION 1. Schema + Resolver => Execution Plan (tree of Steps) 2. Filter Plan to include only requested fields 3. Reduce Plan to pure values and effects 4. Run effects
  • 30. N+1 PROBLEM { orders { # fetches orders (1 query) id customer { # fetches customer (n queries) name } } }
  • 31. QUERY OPTIMIZATION Naive ZIO[R, E, A] version val getAllUserIds: ZIO[Any, Nothing, List[Int]] = ??? def getUserNameById(id: Int): ZIO[Any, Nothing, String] = ??? for { userIds <- getAllUserIds userNames <- ZIO.foreachPar(userIds)(getUserNameById) } yield userNames
  • 32. QUERY OPTIMIZATION Meet ZQuery[R, E, A] val getAllUserIds: ZQuery[Any, Nothing, List[Int]] = ??? def getUserNameById(id: Int): ZQuery[Any, Nothing, String] = ??? for { userIds <- getAllUserIds userNames <- ZQuery.foreachPar(userIds)(getUserNameById) } yield userNames
  • 33. ZQUERY BENEFITS > parallelize queries > cache identical queries (deduplication) > batch queries if batching function provided
  • 34. ZQUERY > courtesy of @adamgfraser > based on paper on Haxl > "There is no Fork: An Abstraction for Efficient, Concurrent, and Concise Data Access" > will be extracted to its own library at some point
  • 35. INTROSPECTION Dogfooding case class __Introspection( __schema: __Schema, __type: __TypeArgs => __Type)
  • 36. USAGE 1. Define your queries / mutations / subscriptions 2. Provide a Schema for custom types 3. Provide a resolver 4. Profit
  • 37. 1. DEFINE YOUR QUERIES case class Pug(name: String, nicknames: List[String], favoriteFood: Option[String]) case class PugName(name: NonEmptyString) case class Queries(pugs: Task[List[Pug]], pug: PugName => Task[Pug]) type Pug { name: String! nicknames: [String!]! favoriteFood: String } type Queries { pugs: [Pug!] pug(name: String!): Pug }
  • 38. 2. PROVIDE A SCHEMA FOR CUSTOM TYPES implicit val nesSchema: Schema[NonEmptyString] = Schema.stringSchema.contramap(_.value)
  • 39. 3. PROVIDE A RESOLVER def getPugs: Task[List[Pug]] = ??? def getPug(name: String): Task[Pug] = ??? val queries = Queries( getPugs, args => getPug(args.name)) val resolver = RootResolver(queries)
  • 40. 4. PROFIT val interpreter = graphQL(resolver) for { result <- interpreter.execute(query) _ <- zio.console.putStrLn(result.toString) } yield ()
  • 41. HTTP4S MODULE val route: HttpRoutes[RIO[R, *]] = Http4sAdapter.makeRestService(interpreter) val wsRoute: HttpRoutes[RIO[R, *]] = Http4sAdapter.makeWebSocketService(interpreter)
  • 42. SCALAJS Few and modern dependencies ScalaJS support for free
  • 43. CATS COMPATIBILITY caliban-cats module def executeAsync[F[_]: Async](...)( implicit runtime: Runtime[Any]): F[GraphQLResponse[E]] def makeRestServiceF[F[_]: Effect, Q, M, S, E](...)( implicit runtime: Runtime[Any]): HttpRoutes[F]
  • 44. PERFORMANCE Caliban is fast Check benchmarks module on github
  • 45. FUTURE > Query analysis > Middleware (query-level, field-level) > Code generation > Other HTTP adapters (Akka HTTP) <- help needed > Client support
  • 46. WANNA KNOW MORE? > Website: https://ghostdogpr.github.io/caliban/ > ZIO Discord: #caliban