SlideShare a Scribd company logo
THE FUNCTIONAL WEB STACK
HTTP4S, DOOBIE & CIRCE
Gary Coady
gcoady@gilt.com
LET’S BUILD A WEBSITE
• It’s going to be CRUD (create/read/update/delete)
• How do we convert data to/from database structure?
• How do we convert data to/from JSON?
• Converting from one format to another is most of a developer’s job!
CONVERTING TO/FROM JSON
String
Instant
Int
case class MyClass(
s: String,
t: Instant,
i: Int)
“s”@JSON String
“t”@JSON Number
“i”@JSON Number
Json.obj(
“s”: Json.str,
“t”: Json.number,
“i”: Json.number)
Case Class JSON Object
CONVERTING TO/FROM JDBC
String
Instant
Int
case class MyClass(
s: String,
t: Instant,
i: Int)
[0]@text
[1]@timestamptz
[2]@bigint
JDBC ResultSet(
0: text,
1: timestamptz,
2: bigint)
Case Class ResultSet
JDBC AND JSON COMBINED
String
Instant
Int
case class MyClass(
s: String,
t: Instant,
i: Int)
[0]@text
[1]@timestamptz
[2]@bigint
JDBC ResultSet(
0: text,
1: timestamptz,
2: bigint)
Case Class ResultSet
“s”@JSON String
“t”@JSON Number
“i”@JSON Number
JSON Object
Json.obj(
“s”: Json.str,
“t”: Json.number,
“i”: Json.number)
JSON CONVERSION WITH CIRCE
• Very fast, usable JSON library
• Automatic derivation of JSON codecs for case classes
• Integration available with http4s
• http://circe.io
JSON CONVERSION WITH CIRCE
trait Encoder[A] {

def apply(a: A): Json

}
A => Json
java.time.Instant => Json
implicit val jsonEncoderInstant: Encoder[Instant] =

Encoder[Long].contramap(_.toEpochMilli)
JSON CONVERSION WITH CIRCE
trait Decoder[A] {

def apply(c: HCursor): Decoder.Result[A]

}
Json => A
implicit val jsonDecoderInstant: Decoder[Instant] =

Decoder[Long].map(Instant.ofEpochMilli)
Json => java.time.Instant
CONVERTING CASE CLASSES WITH CIRCE
Automatic
import io.circe.generic.auto._
Manual
// for case class Person(id: Int, name: String)
object Person {

implicit val decodePerson: Decoder[Person] =

Decoder.forProduct2("id", "name")(Person.apply)



implicit val encodePerson: Encoder[Person] =

Encoder.forProduct2("id", "name")(p =>

(p.id, p.name)

)

}
CONVERTING TO/FROM JSON
String
Instant
Int
case class MyClass(
s: String,
t: Instant,
i: Int)
“s”@JSON String
“t”@JSON Number
“i”@JSON Number
Json.obj(
“s”: Json.str,
“t”: Json.number,
“i”: Json.number)
Case Class JSON Object
JDBC USING DOOBIE
• Doobie is a “pure functional JDBC layer for Scala”
• Complete representation of Java JDBC API
• https://github.com/tpolecat/doobie
• tpolecat.github.io/doobie-0.2.3/00-index.html (Documentation)
JDBC MAPPING WITH DOOBIE
sealed trait Meta[A] {

/** Destination JDBC types to which values of type `A` can be written. */

def jdbcTarget: NonEmptyList[JdbcType]



/** Source JDBC types from which values of type `A` can be read. */

def jdbcSource: NonEmptyList[JdbcType]



/** Constructor for a `getXXX` operation for type `A` at a given index. */

val get: Int => RS.ResultSetIO[A]


/** Constructor for a `setXXX` operation for a given `A` at a given index. */

val set: (Int, A) => PS.PreparedStatementIO[Unit] 



/** Constructor for an `updateXXX` operation for a given `A` at a given index. */

val update: (Int, A) => RS.ResultSetIO[Unit] 



/** Constructor for a `setNull` operation for the primary JDBC type, at a given index. */

val setNull: Int => PS.PreparedStatementIO[Unit]

}
JDBC MAPPING WITH DOOBIE
implicit val metaInstant =

Meta[java.sql.Timestamp].nxmap(

_.toInstant,

(i: Instant) => new java.sql.Timestamp(i.toEpochMilli)

)
Reusing an existing Meta definition
Meta definitions exist for:
Byte
Short
Int
Boolean
String
Array[Byte]
BigDecimal
Long
Float
Double
java.math.BigDecimal
java.sql.Time
java.sql.TimeStamp
java.sql.Date
java.util.Date
CONVERTING CASE CLASSES WITH DOOBIE
Nothing extra needed!
QUERIES WITH DOOBIE
sql"select id, name from people".query[Person]
Table "public.people"
Column | Type | Modifiers
---------------+--------------------------+-----------------------------------------------------
id | integer | not null default nextval('people_id_seq'::regclass)
name | text |
Indexes:
"people_pkey" PRIMARY KEY, btree (id)
Given the table
Define a query
sql"select id, name from people where id = $id".query[Person]
Using prepared statements & variable interpolation
DEFINE THE EXPECTED RESULT SIZE
• myQuery.unique — Expect a single row from the query
• myQuery.option — Expect 0-1 rows from the query, return an Option
• myQuery.list — Return the results as a List
• myQuery.vector — Return the results as a Vector
• myQuery.process — Return the results as a stream of data
RUNNING QUERIES WITH DOOBIE
Define a Transactor for your Database
val xa = DriverManagerTransactor[Task](

"org.postgresql.Driver", "jdbc:postgresql:demo", "demo", ""

)
Run your query using the Transactor
myQuery.list.transact(xa)
Your query runs in a transaction (rollback on uncaught exception)
UPDATES WITH DOOBIE
Call .update instead of .query (returns number of rows modified)
def updatePerson(id: Int, name: String): ConnectionIO[Int] =
sql"update people set name=$name where id=$id"

.update
def updatePerson(id: Int, name: String): ConnectionIO[Person] =
sql"update people set name=$name where id=$id"

.update

.withUniqueGeneratedKeys("id", "name")
.withUniqueGeneratedKeys provides data from updated row
.withGeneratedKeys provides a stream of data, when multiple rows are updated
CONVERTING TO/FROM JDBC
String
Instant
Int
case class MyClass(
s: String,
t: Instant,
i: Int)
[0]@text
[1]@timestamptz
[2]@bigint
JDBC ResultSet(
0: text,
1: timestamptz,
2: bigint)
Case Class ResultSet
JDBC AND JSON COMBINED
String
Instant
Int
case class MyClass(
s: String,
t: Instant,
i: Int)
[0]@text
[1]@timestamptz
[2]@bigint
JDBC ResultSet(
0: text,
1: timestamptz,
2: bigint)
Case Class ResultSet
“s”@JSON String
“t”@JSON Number
“i”@JSON Number
JSON Object
Json.obj(
“s”: Json.str,
“t”: Json.number,
“i”: Json.number)
HTTP4S
• http4s is a typeful, purely functional HTTP library for client and server
applications written in Scala
• http4s.org/
• https://github.com/http4s/http4s
• https://github.com/TechEmpower/FrameworkBenchmarks/tree/master/
frameworks/Scala/http4s (from TechEmpower benchmarks)
• www.lyranthe.org/http4s/ (some programming guides)
WRITING A WEB SERVICE WITH HTTP4S
type HttpService = Service[Request, Response]
represents
Request => Task[Response]
Three requirements:
• Setup service
• Parse request
• Generate response
PATTERN MATCHING ON THE REQUEST
<METHOD> -> <PATH>
Extract Method and Path from Request
For example
case GET -> path
Root / "people" / id
Extract path components from Path
PATTERN MATCHING ON THE REQUEST
Extract typed components with extractors
import java.util.UUID



object UUIDVar {

def unapply(s: String): Option[UUID] = {

try {

UUID.fromString(s)

} catch {

case e: IllegalArgumentException =>

None

}

}

}
Root / "people" / UUIDVar(uuid)
PARSING REQUEST BODY
Register Circe as JSON decoder
implicit def circeJsonDecoder[A](implicit decoder: Decoder[A]) =
org.http4s.circe.jsonOf[A]
req.decode[Person] { person =>
...
}
Decode to a Person object
CREATING RESPONSE
Ok(Person(1, "Name"))
Output response code and any type with an EntityEncoder
Ok("Hello world")
Output response code and String as body
implicit def circeJsonEncoder[A](implicit encoder: Encoder[A]) =
org.http4s.circe.jsonEncoderOf[A]
PUTTING IT ALL TOGETHER
case class Person(id: Int, firstName: String, familyName: String, registeredAt: Instant)

case class PersonForm(firstName: String, familyName: String)
object PersonDAO {

implicit val metaInstant =

Meta[java.sql.Timestamp].nxmap(

_.toInstant,

(i: Instant) => new java.sql.Timestamp(i.toEpochMilli)

)



val listPeople: ConnectionIO[List[Person]] =

sql"select id, first_name, family_name, registered_at from people"

.query[Person]

.list



def getPerson(id: Long): ConnectionIO[Option[Person]] =

sql"select id, first_name, family_name, registered_at from people where id = $id"

.query[Person]

.option



def updatePerson(id: Int, firstName: String, familyName: String): ConnectionIO[Person] =

sql"update people set first_name=$firstName, family_name=$familyName where id=$id"

.update

.withUniqueGeneratedKeys("id", "first_name", "family_name", "registered_at")



def insertPerson(firstName: String, familyName: String, registeredAt: Instant = Instant.now()): ConnectionIO[Person] =

sql"insert into people (first_name, family_name, registered_at) values ($firstName, $familyName, $registeredAt)"

.update

.withUniqueGeneratedKeys("id", "first_name", "family_name", "registered_at")

}
PUTTING IT ALL TOGETHER
case class Person(id: Int, firstName: String, familyName: String, registeredAt: Instant)

case class PersonForm(firstName: String, familyName: String)
object DemoService {

implicit def circeJsonDecoder[A](implicit decoder: Decoder[A]) =
org.http4s.circe.jsonOf[A]


implicit def circeJsonEncoder[A](implicit encoder: Encoder[A]) =
org.http4s.circe.jsonEncoderOf[A]



def service(xa: Transactor[Task]) = HttpService {

case GET -> Root / "people" =>

Ok(PersonDAO.listPeople.transact(xa))



case GET -> Root / "people" / IntVar(id) =>

for {

person <- PersonDAO.getPerson(id).transact(xa)

result <- person.fold(NotFound())(Ok.apply)

} yield result



case req @ PUT -> Root / "people" / IntVar(id) =>

req.decode[PersonForm] { form =>

Ok(PersonDAO.updatePerson(id, form.firstName, form.familyName).transact(xa))

}



case req @ POST -> Root / "people" =>

req.decode[PersonForm] { form =>

Ok(PersonDAO.insertPerson(form.firstName, form.familyName).transact(xa))

}

}

}
PUTTING IT ALL TOGETHER
object Main {

def main(args: Array[String]): Unit = {

val xa = DriverManagerTransactor[Task](

"org.postgresql.Driver", "jdbc:postgresql:demo", "demo", ""

)



val server =

BlazeBuilder
.bindHttp(8080)

.mountService(DemoService.service(xa))

.run



server.awaitShutdown()

}

}
ADDING HTML TEMPLATING WITH TWIRL
Add to project/plugins.sbt
addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.1.1")
Place templates in src/main/twirl
PACKAGING WITH SBT-NATIVE-PACKAGER
Add to project/plugins.sbt
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.1.0-RC1")
Enable AutoPlugin
enablePlugins(JavaServerAppPackaging)
STREAMING DATA WITH SCALAZ-STREAM
• Response can take a scalaz-stream Process
• Doobie can create a scalaz-stream Process from a Resultset
• Data sent incrementally using chunked Transfer-Encoding
val streamPeople: Process[ConnectionIO, Person] =

sql"select id, first_name, family_name, registered_at from people"

.query[Person]

.process
case GET -> Root / "stream" =>

Ok(PersonDAO.streamPeople.transact(xa).map(p => p.id + "n"))
QUESTIONS?
https://github.com/fiadliel/http4s-talk

More Related Content

PDF
Functional Error Handling with Cats
PDF
Left and Right Folds - Comparison of a mathematical definition and a programm...
PDF
Applicative Functor
PDF
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
PPTX
Normal distribution slide share
PPT
L5 infinite limits squeeze theorem
ODT
PPT
Lesson 14 a - parametric equations
Functional Error Handling with Cats
Left and Right Folds - Comparison of a mathematical definition and a programm...
Applicative Functor
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
Normal distribution slide share
L5 infinite limits squeeze theorem
Lesson 14 a - parametric equations

What's hot (20)

PPTX
Binomial Theorem
PDF
Limits, Continuity & Differentiation (Theory)
PPT
Classical probability
PPTX
CMSC 56 | Lecture 4: Rules of Inference
PDF
1.1 Linear Equations
PPT
Second derivative test ap calc
PDF
Ad hoc Polymorphism using Type Classes and Cats
PPTX
Permutations and Combinations.pptx
PPS
Functions and graphs
PPTX
Taking your side effects aside
PPT
Increasing and decreasing functions ap calc sec 3.3
PPTX
7 functions
PPTX
Geometric probability distribution
PDF
Solución Álgebra Lineal 2017 2S 2do Parcial
PPT
Fundamentals Probability 08072009
PPTX
Fourier series and fourier integral
PDF
Functional Programming 101 with Scala and ZIO @FunctionalWorld
PPTX
1.6 slopes and the difference quotient
PPTX
AUTOMORPHISMS With Examples.pptx
PDF
Lesson 7: Limits at Infinity
Binomial Theorem
Limits, Continuity & Differentiation (Theory)
Classical probability
CMSC 56 | Lecture 4: Rules of Inference
1.1 Linear Equations
Second derivative test ap calc
Ad hoc Polymorphism using Type Classes and Cats
Permutations and Combinations.pptx
Functions and graphs
Taking your side effects aside
Increasing and decreasing functions ap calc sec 3.3
7 functions
Geometric probability distribution
Solución Álgebra Lineal 2017 2S 2do Parcial
Fundamentals Probability 08072009
Fourier series and fourier integral
Functional Programming 101 with Scala and ZIO @FunctionalWorld
1.6 slopes and the difference quotient
AUTOMORPHISMS With Examples.pptx
Lesson 7: Limits at Infinity
Ad

Similar to Http4s, Doobie and Circe: The Functional Web Stack (20)

PDF
Going Native: Leveraging the New JSON Native Datatype in Oracle 21c
PDF
Spray Json and MongoDB Queries: Insights and Simple Tricks.
PPT
3 database-jdbc(1)
PDF
Data access 2.0? Please welcome: Spring Data!
PDF
JSON Fuzzing: New approach to old problems
PDF
Persisting Data on SQLite using Room
PDF
ScalikeJDBC Tutorial for Beginners
PPTX
Database Access With JDBC
PPT
Executing Sql Commands
PPT
Executing Sql Commands
PDF
OrientDB - The 2nd generation of (multi-model) NoSQL
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
KEY
Jython: Python para la plataforma Java (EL2009)
PDF
Lecture17
KEY
CouchDB on Android
PDF
NoSQL and JavaScript: a Love Story
PDF
Requery overview
Going Native: Leveraging the New JSON Native Datatype in Oracle 21c
Spray Json and MongoDB Queries: Insights and Simple Tricks.
3 database-jdbc(1)
Data access 2.0? Please welcome: Spring Data!
JSON Fuzzing: New approach to old problems
Persisting Data on SQLite using Room
ScalikeJDBC Tutorial for Beginners
Database Access With JDBC
Executing Sql Commands
Executing Sql Commands
OrientDB - The 2nd generation of (multi-model) NoSQL
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Jython: Python para la plataforma Java (EL2009)
Lecture17
CouchDB on Android
NoSQL and JavaScript: a Love Story
Requery overview
Ad

Recently uploaded (20)

PPTX
CARTOGRAPHY AND GEOINFORMATION VISUALIZATION chapter1 NPTE (2).pptx
PDF
Well-logging-methods_new................
PDF
Digital Logic Computer Design lecture notes
PPTX
OOP with Java - Java Introduction (Basics)
PDF
keyrequirementskkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
PPT
Mechanical Engineering MATERIALS Selection
PPTX
Sustainable Sites - Green Building Construction
PPTX
Engineering Ethics, Safety and Environment [Autosaved] (1).pptx
PPTX
Construction Project Organization Group 2.pptx
PDF
Mohammad Mahdi Farshadian CV - Prospective PhD Student 2026
PDF
Mitigating Risks through Effective Management for Enhancing Organizational Pe...
PPTX
Recipes for Real Time Voice AI WebRTC, SLMs and Open Source Software.pptx
DOCX
573137875-Attendance-Management-System-original
PDF
SM_6th-Sem__Cse_Internet-of-Things.pdf IOT
PPTX
M Tech Sem 1 Civil Engineering Environmental Sciences.pptx
PPTX
additive manufacturing of ss316l using mig welding
PDF
Embodied AI: Ushering in the Next Era of Intelligent Systems
PPTX
Strings in CPP - Strings in C++ are sequences of characters used to store and...
PDF
Arduino robotics embedded978-1-4302-3184-4.pdf
PPTX
KTU 2019 -S7-MCN 401 MODULE 2-VINAY.pptx
CARTOGRAPHY AND GEOINFORMATION VISUALIZATION chapter1 NPTE (2).pptx
Well-logging-methods_new................
Digital Logic Computer Design lecture notes
OOP with Java - Java Introduction (Basics)
keyrequirementskkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
Mechanical Engineering MATERIALS Selection
Sustainable Sites - Green Building Construction
Engineering Ethics, Safety and Environment [Autosaved] (1).pptx
Construction Project Organization Group 2.pptx
Mohammad Mahdi Farshadian CV - Prospective PhD Student 2026
Mitigating Risks through Effective Management for Enhancing Organizational Pe...
Recipes for Real Time Voice AI WebRTC, SLMs and Open Source Software.pptx
573137875-Attendance-Management-System-original
SM_6th-Sem__Cse_Internet-of-Things.pdf IOT
M Tech Sem 1 Civil Engineering Environmental Sciences.pptx
additive manufacturing of ss316l using mig welding
Embodied AI: Ushering in the Next Era of Intelligent Systems
Strings in CPP - Strings in C++ are sequences of characters used to store and...
Arduino robotics embedded978-1-4302-3184-4.pdf
KTU 2019 -S7-MCN 401 MODULE 2-VINAY.pptx

Http4s, Doobie and Circe: The Functional Web Stack

  • 1. THE FUNCTIONAL WEB STACK HTTP4S, DOOBIE & CIRCE Gary Coady gcoady@gilt.com
  • 2. LET’S BUILD A WEBSITE • It’s going to be CRUD (create/read/update/delete) • How do we convert data to/from database structure? • How do we convert data to/from JSON? • Converting from one format to another is most of a developer’s job!
  • 3. CONVERTING TO/FROM JSON String Instant Int case class MyClass( s: String, t: Instant, i: Int) “s”@JSON String “t”@JSON Number “i”@JSON Number Json.obj( “s”: Json.str, “t”: Json.number, “i”: Json.number) Case Class JSON Object
  • 4. CONVERTING TO/FROM JDBC String Instant Int case class MyClass( s: String, t: Instant, i: Int) [0]@text [1]@timestamptz [2]@bigint JDBC ResultSet( 0: text, 1: timestamptz, 2: bigint) Case Class ResultSet
  • 5. JDBC AND JSON COMBINED String Instant Int case class MyClass( s: String, t: Instant, i: Int) [0]@text [1]@timestamptz [2]@bigint JDBC ResultSet( 0: text, 1: timestamptz, 2: bigint) Case Class ResultSet “s”@JSON String “t”@JSON Number “i”@JSON Number JSON Object Json.obj( “s”: Json.str, “t”: Json.number, “i”: Json.number)
  • 6. JSON CONVERSION WITH CIRCE • Very fast, usable JSON library • Automatic derivation of JSON codecs for case classes • Integration available with http4s • http://circe.io
  • 7. JSON CONVERSION WITH CIRCE trait Encoder[A] {
 def apply(a: A): Json
 } A => Json java.time.Instant => Json implicit val jsonEncoderInstant: Encoder[Instant] =
 Encoder[Long].contramap(_.toEpochMilli)
  • 8. JSON CONVERSION WITH CIRCE trait Decoder[A] {
 def apply(c: HCursor): Decoder.Result[A]
 } Json => A implicit val jsonDecoderInstant: Decoder[Instant] =
 Decoder[Long].map(Instant.ofEpochMilli) Json => java.time.Instant
  • 9. CONVERTING CASE CLASSES WITH CIRCE Automatic import io.circe.generic.auto._ Manual // for case class Person(id: Int, name: String) object Person {
 implicit val decodePerson: Decoder[Person] =
 Decoder.forProduct2("id", "name")(Person.apply)
 
 implicit val encodePerson: Encoder[Person] =
 Encoder.forProduct2("id", "name")(p =>
 (p.id, p.name)
 )
 }
  • 10. CONVERTING TO/FROM JSON String Instant Int case class MyClass( s: String, t: Instant, i: Int) “s”@JSON String “t”@JSON Number “i”@JSON Number Json.obj( “s”: Json.str, “t”: Json.number, “i”: Json.number) Case Class JSON Object
  • 11. JDBC USING DOOBIE • Doobie is a “pure functional JDBC layer for Scala” • Complete representation of Java JDBC API • https://github.com/tpolecat/doobie • tpolecat.github.io/doobie-0.2.3/00-index.html (Documentation)
  • 12. JDBC MAPPING WITH DOOBIE sealed trait Meta[A] {
 /** Destination JDBC types to which values of type `A` can be written. */
 def jdbcTarget: NonEmptyList[JdbcType]
 
 /** Source JDBC types from which values of type `A` can be read. */
 def jdbcSource: NonEmptyList[JdbcType]
 
 /** Constructor for a `getXXX` operation for type `A` at a given index. */
 val get: Int => RS.ResultSetIO[A] 
 /** Constructor for a `setXXX` operation for a given `A` at a given index. */
 val set: (Int, A) => PS.PreparedStatementIO[Unit] 
 
 /** Constructor for an `updateXXX` operation for a given `A` at a given index. */
 val update: (Int, A) => RS.ResultSetIO[Unit] 
 
 /** Constructor for a `setNull` operation for the primary JDBC type, at a given index. */
 val setNull: Int => PS.PreparedStatementIO[Unit]
 }
  • 13. JDBC MAPPING WITH DOOBIE implicit val metaInstant =
 Meta[java.sql.Timestamp].nxmap(
 _.toInstant,
 (i: Instant) => new java.sql.Timestamp(i.toEpochMilli)
 ) Reusing an existing Meta definition Meta definitions exist for: Byte Short Int Boolean String Array[Byte] BigDecimal Long Float Double java.math.BigDecimal java.sql.Time java.sql.TimeStamp java.sql.Date java.util.Date
  • 14. CONVERTING CASE CLASSES WITH DOOBIE Nothing extra needed!
  • 15. QUERIES WITH DOOBIE sql"select id, name from people".query[Person] Table "public.people" Column | Type | Modifiers ---------------+--------------------------+----------------------------------------------------- id | integer | not null default nextval('people_id_seq'::regclass) name | text | Indexes: "people_pkey" PRIMARY KEY, btree (id) Given the table Define a query sql"select id, name from people where id = $id".query[Person] Using prepared statements & variable interpolation
  • 16. DEFINE THE EXPECTED RESULT SIZE • myQuery.unique — Expect a single row from the query • myQuery.option — Expect 0-1 rows from the query, return an Option • myQuery.list — Return the results as a List • myQuery.vector — Return the results as a Vector • myQuery.process — Return the results as a stream of data
  • 17. RUNNING QUERIES WITH DOOBIE Define a Transactor for your Database val xa = DriverManagerTransactor[Task](
 "org.postgresql.Driver", "jdbc:postgresql:demo", "demo", ""
 ) Run your query using the Transactor myQuery.list.transact(xa) Your query runs in a transaction (rollback on uncaught exception)
  • 18. UPDATES WITH DOOBIE Call .update instead of .query (returns number of rows modified) def updatePerson(id: Int, name: String): ConnectionIO[Int] = sql"update people set name=$name where id=$id"
 .update def updatePerson(id: Int, name: String): ConnectionIO[Person] = sql"update people set name=$name where id=$id"
 .update
 .withUniqueGeneratedKeys("id", "name") .withUniqueGeneratedKeys provides data from updated row .withGeneratedKeys provides a stream of data, when multiple rows are updated
  • 19. CONVERTING TO/FROM JDBC String Instant Int case class MyClass( s: String, t: Instant, i: Int) [0]@text [1]@timestamptz [2]@bigint JDBC ResultSet( 0: text, 1: timestamptz, 2: bigint) Case Class ResultSet
  • 20. JDBC AND JSON COMBINED String Instant Int case class MyClass( s: String, t: Instant, i: Int) [0]@text [1]@timestamptz [2]@bigint JDBC ResultSet( 0: text, 1: timestamptz, 2: bigint) Case Class ResultSet “s”@JSON String “t”@JSON Number “i”@JSON Number JSON Object Json.obj( “s”: Json.str, “t”: Json.number, “i”: Json.number)
  • 21. HTTP4S • http4s is a typeful, purely functional HTTP library for client and server applications written in Scala • http4s.org/ • https://github.com/http4s/http4s • https://github.com/TechEmpower/FrameworkBenchmarks/tree/master/ frameworks/Scala/http4s (from TechEmpower benchmarks) • www.lyranthe.org/http4s/ (some programming guides)
  • 22. WRITING A WEB SERVICE WITH HTTP4S type HttpService = Service[Request, Response] represents Request => Task[Response] Three requirements: • Setup service • Parse request • Generate response
  • 23. PATTERN MATCHING ON THE REQUEST <METHOD> -> <PATH> Extract Method and Path from Request For example case GET -> path Root / "people" / id Extract path components from Path
  • 24. PATTERN MATCHING ON THE REQUEST Extract typed components with extractors import java.util.UUID
 
 object UUIDVar {
 def unapply(s: String): Option[UUID] = {
 try {
 UUID.fromString(s)
 } catch {
 case e: IllegalArgumentException =>
 None
 }
 }
 } Root / "people" / UUIDVar(uuid)
  • 25. PARSING REQUEST BODY Register Circe as JSON decoder implicit def circeJsonDecoder[A](implicit decoder: Decoder[A]) = org.http4s.circe.jsonOf[A] req.decode[Person] { person => ... } Decode to a Person object
  • 26. CREATING RESPONSE Ok(Person(1, "Name")) Output response code and any type with an EntityEncoder Ok("Hello world") Output response code and String as body implicit def circeJsonEncoder[A](implicit encoder: Encoder[A]) = org.http4s.circe.jsonEncoderOf[A]
  • 27. PUTTING IT ALL TOGETHER case class Person(id: Int, firstName: String, familyName: String, registeredAt: Instant)
 case class PersonForm(firstName: String, familyName: String) object PersonDAO {
 implicit val metaInstant =
 Meta[java.sql.Timestamp].nxmap(
 _.toInstant,
 (i: Instant) => new java.sql.Timestamp(i.toEpochMilli)
 )
 
 val listPeople: ConnectionIO[List[Person]] =
 sql"select id, first_name, family_name, registered_at from people"
 .query[Person]
 .list
 
 def getPerson(id: Long): ConnectionIO[Option[Person]] =
 sql"select id, first_name, family_name, registered_at from people where id = $id"
 .query[Person]
 .option
 
 def updatePerson(id: Int, firstName: String, familyName: String): ConnectionIO[Person] =
 sql"update people set first_name=$firstName, family_name=$familyName where id=$id"
 .update
 .withUniqueGeneratedKeys("id", "first_name", "family_name", "registered_at")
 
 def insertPerson(firstName: String, familyName: String, registeredAt: Instant = Instant.now()): ConnectionIO[Person] =
 sql"insert into people (first_name, family_name, registered_at) values ($firstName, $familyName, $registeredAt)"
 .update
 .withUniqueGeneratedKeys("id", "first_name", "family_name", "registered_at")
 }
  • 28. PUTTING IT ALL TOGETHER case class Person(id: Int, firstName: String, familyName: String, registeredAt: Instant)
 case class PersonForm(firstName: String, familyName: String) object DemoService {
 implicit def circeJsonDecoder[A](implicit decoder: Decoder[A]) = org.http4s.circe.jsonOf[A] 
 implicit def circeJsonEncoder[A](implicit encoder: Encoder[A]) = org.http4s.circe.jsonEncoderOf[A]
 
 def service(xa: Transactor[Task]) = HttpService {
 case GET -> Root / "people" =>
 Ok(PersonDAO.listPeople.transact(xa))
 
 case GET -> Root / "people" / IntVar(id) =>
 for {
 person <- PersonDAO.getPerson(id).transact(xa)
 result <- person.fold(NotFound())(Ok.apply)
 } yield result
 
 case req @ PUT -> Root / "people" / IntVar(id) =>
 req.decode[PersonForm] { form =>
 Ok(PersonDAO.updatePerson(id, form.firstName, form.familyName).transact(xa))
 }
 
 case req @ POST -> Root / "people" =>
 req.decode[PersonForm] { form =>
 Ok(PersonDAO.insertPerson(form.firstName, form.familyName).transact(xa))
 }
 }
 }
  • 29. PUTTING IT ALL TOGETHER object Main {
 def main(args: Array[String]): Unit = {
 val xa = DriverManagerTransactor[Task](
 "org.postgresql.Driver", "jdbc:postgresql:demo", "demo", ""
 )
 
 val server =
 BlazeBuilder .bindHttp(8080)
 .mountService(DemoService.service(xa))
 .run
 
 server.awaitShutdown()
 }
 }
  • 30. ADDING HTML TEMPLATING WITH TWIRL Add to project/plugins.sbt addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.1.1") Place templates in src/main/twirl
  • 31. PACKAGING WITH SBT-NATIVE-PACKAGER Add to project/plugins.sbt addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.1.0-RC1") Enable AutoPlugin enablePlugins(JavaServerAppPackaging)
  • 32. STREAMING DATA WITH SCALAZ-STREAM • Response can take a scalaz-stream Process • Doobie can create a scalaz-stream Process from a Resultset • Data sent incrementally using chunked Transfer-Encoding val streamPeople: Process[ConnectionIO, Person] =
 sql"select id, first_name, family_name, registered_at from people"
 .query[Person]
 .process case GET -> Root / "stream" =>
 Ok(PersonDAO.streamPeople.transact(xa).map(p => p.id + "n"))