Introduction
This is a very short book about doobie, which is a pure-functional JDBC layer for Scala.
doobie provides low-level access to everything in java.sql
(as of Java 8), allowing you to write any JDBC program in a pure functional style. However the focus of this book is the high-level API, which is where most users will spend their time.
This book is organized cookbook-style: we demonstrate a common task and then explain how it works, perhaps in more detail than you want right now. The goal is to get you up and running quickly, but give you a handle on the deeper stuff if you need it later.
Target Audience
This library is designed for people who are interested in typed, pure functional programming. If you are not a Cats user or are not familiar with functional I/O and monadic effects, you may need to go slowly and may want to spend some time reading Functional Programming in Scala, which introduces all of the ideas that you will find when exploring doobie.
Having said this, if you find yourself confused or frustrated by this documentation or the doobie API, please ask a question on Gitter, file an issue and ask for help. Both the library and the documentation are young and are changing quickly, and it is inevitable that some things will be unclear. Accordingly, this book is updated continuously to address problems and omissions.
The Setup
This book is compiled as part of the build using the tut tutorial generator, so the code examples are guaranteed to compile (and with luck should also work correctly). Each page stands on its own: if you copy and paste code samples starting from the top, it will work in your REPL as long as you have the proper setup, described here.
Sample Database Setup
The example code assumes a local PostgreSQL server with a postgres
user with no password, PostGIS extensions (optional), and the sample world
database loaded up. If you’re on a Mac you might check out the excellent Postgres.app if you don’t want to install PostgreSQL as a service. You can set up the user and sample database (and an enum
we use in a few examples) as follows:
$ curl -O https://raw.githubusercontent.com/tpolecat/doobie/series/0.7.x/world.sql
$ psql -c 'create user postgres createdb' postgres
$ psql -c 'create database world;' -U postgres
$ psql -c '\i world.sql' -d world -U postgres
$ psql -d world -c "create type myenum as enum ('foo', 'bar')" -U postgres
$ psql -d world -c "create extension postgis" -U postgres
Skip the last statement if you don’t have PostGIS installed. Note that the final ANALYZE
comand in the import will emit a few errors for system tables. This is expected and is fine. Try a query or two to double-check your setup:
$ psql -d world -U postgres
psql (9.3.5)
Type "help" for help.
world=> select name, continent, population from country where name like 'U%';
name | continent | population
--------------------------------------+---------------+------------
United Arab Emirates | Asia | 2441000
United Kingdom | Europe | 59623400
Uganda | Africa | 21778000
Ukraine | Europe | 50456000
Uruguay | South America | 3337000
Uzbekistan | Asia | 24318000
United States | North America | 278357000
United States Minor Outlying Islands | Oceania | 0
(8 rows)
world=> \q
$
You can of course change this setup if you like, but you will need to adjust your JDBC connection information accordingly. Most examples will work with any compliant database, but in a few cases (noted in the text) we rely on vendor-specific behavior.
Scala Setup
On the Scala side you just need a console with the proper dependencies. A minimal build.sbt
would look something like this.
scalaVersion := "2.13.11" // Scala 2.13.11
lazy val doobieVersion = "1.0.0-RC4"
libraryDependencies ++= Seq(
"org.tpolecat" %% "doobie-core" % doobieVersion,
"org.tpolecat" %% "doobie-postgres" % doobieVersion,
"org.tpolecat" %% "doobie-specs2" % doobieVersion
)
The -Ypartial-unification
compiler flag enables a bug fix that makes working with functional code significantly easier. See the Cats Getting Started for more info on this if it interests you. If you’re using Scala 2.13+ the compiler no longer accepts that option.
If you are not using PostgreSQL you can omit doobie-postgres
and will need to add the appropriate JDBC driver as a dependency. Note that there is a doobie-h2
add-on if you happen to be using H2.
Conventions
Each page begins with some imports, like this.
import cats._, cats.data._, cats.implicits._
import doobie._
After that there is text interspersed with code examples. Sometimes definitions will stand alone.
case class Person(name: String, age: Int)
val nel = NonEmptyList.of(Person("Bob", 12), Person("Alice", 14))
And sometimes they will appear with output.
nel.head
// res0: Person = Person(name = "Bob", age = 12)
nel.tail
// res1: List[Person] = List(Person(name = "Alice", age = 14))
Sometimes we demonstrate that something doesn’t compile. In such cases it will be clear from the context that this is expected, and not a problem with the documentation.
woozle(nel) // doesn't compile
// error: not found: value woozle
// woozle(nel) // doesn't compile
// ^^^^^^
Feedback and Contributions
Feedback on doobie or this book is genuinely welcome. Please feel free to file a pull request if you have a contribution, or file an issue, or chat with us on Gitter.