Open-source project
typelevel/doobie avatar
typelevel/doobie

doobie: a JDBC layer for Scala that treats the connection pool as a value

Functional JDBC layer for Scala.

2,224 stars377 forksScalaMIT

At a glance

What is it?
Typelevel's doobie is one line of description long and sends you to a microsite. What the repository does show is a library mid-rename, a three-database integration harness, and an ecosystem that ships adapters as separate artifacts.
Who is it for?
Doobie is the database layer for a Typeclass in Scala stack that wants typed queries and does not want an ORM, and its design follows from treating resources as values rather than as effects to manage by hand. The repository tells you very little about that design on its own, so plan on reading the microsite rather than the README.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Scala, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 7, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A README that is three sentences and a link

The whole technical description in the README is one line: doobie is a pure functional JDBC layer for Scala. Everything else is a pointer to the microsite at typelevel.org/doobie, which the README describes as where to proceed for more information.

That is unusual but consistent with how Typelevel projects are organised. The logo is pinned from a CDN path rather than committed locally as documentation, the badges cover Maven Central, Javadocs and Discord, and the body is a short block followed by two community sections. There is no quick start, no code sample and no feature list in the README at all.

What remains is still worth reading. There is a bug reporting instruction that asks you to use a separate `doobie_quick_start` repository, which should make it easy to reproduce your issue. That tells you something real about how the project handles incoming reports: the maintainers would rather receive a runnable reproduction than a narrative. The community section points at GitHub discussions and a Typelevel Discord channel, and states that people are expected to follow the Scala Code of Conduct in all three venues.

So the README is a signpost rather than documentation, and the useful judgement is whether the signpost points at good documentation. For a library at 1.0.0 release candidate stage, an external microsite is defensible.

The package rename that every existing user has to handle

The newest release, v1.0.0-RC13 from June 2026, is almost entirely about an administrative change with a real migration cost. The notes explain that doobie moved under the Typelevel GitHub organisation some time ago and this release completes the move.

Two things change. Every import should be `org.typelevel.doobie` rather than `doobie`, and the artifact is published under the `org.typelevel` group id rather than `org.tpolecat`. Both are visible in the repository's own metadata: the Maven Central badge in the README points at `org.typelevel/doobie-core_2.13`, while the tree and build files come from the typelevel/doobie location.

The migration path is documented as two steps, and the first one is a scalafix rule rather than a find and replace. You add sbt-scalafix, add the `doobie-package-rename-scalafix` artifact as a scalafix dependency, enable SemanticDB for the project, then run the rule to rewrite references. The second step is updating the dependency in build.sbt.

Shipping a migration tool with the breaking change is the detail that makes this acceptable. A package rename in a library people depend on would normally mean a find and replace across every codebase in the ecosystem, and the cost lands on users rather than on the maintainers. Providing the rule moves that work to whoever has automated tooling, which is most Scala projects.

A docker-compose harness covering three databases and PostGIS

The repository is small: `.git-blame-ignore-revs`, `.github/`, `.mergify.yml`, `.scalafmt.conf`, `build.sbt`, `docker-compose.yml`, `init/`, `modules/` and `project/`. There is no `examples/` and no standalone documentation site in the tree. The interesting file for understanding how the library is tested is the compose file, because it names the databases the library must work against.

yaml
services:
  postgres:
    image: postgis/postgis:16-3.4
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: world
    ports:
      - 5432:5432

Postgres is not a stock image here. It is postgis at version 16-3.4, which means the test harness has a PostGIS-enabled Postgres available, so the spatial types the library exposes are exercised rather than stubbed. The database is named `world`, which is a hint about the sample data used in tests. Memory is capped at 500M per service.

MySQL sits alongside it on 3306, running mysql:8.0 with a root password and a `world` database, and the same 500M memory limit. Both services mount a directory from `init/` into `/docker-entrypoint-initdb.d/`, so schema setup for the tests lives in the repository rather than in a shell command.

For a library whose entire job is abstracting JDBC, that is the correct test matrix. The database is the part that varies, everything above it is the same code, and naming Postgres, MySQL and PostGIS in one compose file tells you which drivers get real attention. SQLite is conspicuously absent from this file.

Modules as separate artifacts, and the tracing work in progress

Doobie publishes as a set of modules rather than one jar, and the `_2.13` in the artifact name tells you the Scala binary version is baked into the coordinates. The README badges reference `doobie-core`, which is the base module, and the release notes for v1.0.0-RC12 describe an `otel4s` module as unpublished initial support for OpenTelemetry in `doobie-otel4s`.

That is the shape of a library that separates the abstraction from the integrations. Core is the JDBC layer with no driver. Database-specific support lives in its own artifact. Observability lives in another. The consequence for a user is that adding a feature often means adding a dependency, and the set of modules to choose from is not documented in the README.

The RC12 notes are otherwise small. They add an `emap` method to `Read`, contributed by an outside developer, alongside the OpenTelemetry work. Read is the typeclass that decodes a database row into a case class, so adding a method to it is a source-compatibility event for anyone implementing it by hand, which is one reason it is worth knowing the change landed in a release candidate.

One more piece of infrastructure is worth noting because it tells you how automated the repository is: `.mergify.yml` in the root. That is a tool for queueing and merging pull requests automatically once they pass their checks, and its presence alongside `.scalafmt.conf` and `.jvmopts` indicates a repository with formatting, JVM flags and merge automation all configured as files.

What the repository does not tell you

The gaps are significant enough to state plainly, because a reader deciding whether to adopt a database layer needs answers this repository does not contain.

There is no statement of which SQL or streaming features are supported, no explanation of how connections are managed or pooled, and no comparison with other Scala database approaches. There is no performance guidance and no statement of the Scala versions supported, even though the artifact suffix shows 2.13 is one of them. There is also no list of the modules, so a reader has to infer from release notes that an OpenTelemetry module exists.

What the repository does supply is metadata worth trusting. The language field says Scala and the license is MIT, which is the permissive choice most consistent with a library meant to be embedded in other people's applications. Stars sit at 2225 with 379 forks, and 140 open issues is a high count for a library, which is consistent with a project still settling into its 1.0 release. The last push was 2026-09-26 and the repository is not archived, so the code is moving.

The topics are the clearest summary available: database, fp, functional-programming, jdbc, scala and typelevel. That is an honest description of a project whose appeal is a specific style rather than a specific feature, and it is the right vocabulary to search on if the README leaves you without a starting point.

Where this fits against the alternatives

A Scala developer choosing a database layer is choosing between a typed query DSL, an ORM, and a thin wrapper over the driver. Doobie is in the first category with functional types, sitting closest to the driver rather than hiding it.

The distinction that decides it is how much of the database you want to keep. An ORM will hide the schema, generate queries and track entity state, which is convenient and which becomes a liability when the schema is the thing you need full control over. A raw JDBC driver gives full control and leaves you writing boilerplate. Doobie sits between them: queries are typed and constructed from the same typeclass machinery that decodes results, and the SQL you write is the SQL that runs.

The cost of that middle position is that you need a decoder for every row type you read, and the library will not tell you what a good decoder looks like. That knowledge lives on the microsite. A second cost is the module split, since a project that adds Postgres and tracing has two more dependencies to manage than one that adds a single jar.

If the decision is between doobie and an ORM, the question to ask is whether the schema or the object graph is the primary thing in your program. If it is the schema, doobie is the closer fit. If you want the database to load your aggregates for you and you do not mind the mapping cost, an ORM will get you there faster.

Editorial conclusion

Doobie is the database layer for a Typeclass in Scala stack that wants typed queries and does not want an ORM, and its design follows from treating resources as values rather than as effects to manage by hand. The repository tells you very little about that design on its own, so plan on reading the microsite rather than the README. Two practical notes come out of the repository directly. First, the 1.0.0 release candidates changed both the package name and the group id, so pin a version deliberately and use the migration rule if you are upgrading. Second, `Transactor` is the entry point and every database needs its own artifact, so choosing a driver is the first decision and not an afterthought.

Frequently asked questions

What do I need to change to migrate to doobie 1.0.0?

Imports become org.typelevel.doobie instead of doobie, and the dependency moves to the org.typelevel group id. The release notes provide a doobie-package-rename-scalafix rule, run through sbt-scalafix with SemanticDB enabled, to rewrite references.

Which databases does doobie test against?

The docker-compose file in the repository runs Postgres as postgis/postgis:16-3.4 and MySQL as mysql:8.0, both with a world database and schema loaded from the init directory.

Does doobie support OpenTelemetry?

There is an unpublished doobie-otel4s module with initial OpenTelemetry support, described in the v1.0.0-RC12 notes. Core is separate from it, so tracing is an added module rather than part of the base library.

Where is the doobie documentation?

The README points to the microsite at typelevel.org/doobie and says to proceed there for more information. The repository itself carries no quick start or code examples.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. typelevel/doobie on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/typelevel-doobie.svg)](https://hysenlabs.com/projects/typelevel-doobie)