Skip to main content

Hello, ZIO

· 2 min read

Thanks to the efforts of Jules Ivanic in PR #57, Typo now supports using zio-jdbc as a database library.

Never heard of Typo?

You can check out the introduction. Essentially it's a code generator for database access code, which makes PostgreSQL integration type-safe and wonderful to use.


Note that zio-jdbc is a bit less mature than doobie and anorm, so it's a bit more likely to be some rough edges. In particular, it handles nullable values imperfectly. We fixed a bunch of issues while working on this PR, so it should be pretty close.

Implemented missing features in zio-jdbc

zio-jdbc does not support postgres arrays, and it does not support the COPY API for streaming inserts.

Typo outputs code which implements both of these features.

This can likely be upstreamed as a postgres integration module in zio-jdbc at some point.

Repository signatures

To give a taste of the code you'll get, consider this repository definition:

trait AddressRepo {
def delete(addressid: AddressId): ZIO[ZConnection, Throwable, Boolean]
def delete: DeleteBuilder[AddressFields, AddressRow]
def insert(unsaved: AddressRow): ZIO[ZConnection, Throwable, AddressRow]
def insertStreaming(unsaved: ZStream[ZConnection, Throwable, AddressRow], batchSize: Int): ZIO[ZConnection, Throwable, Long]
def insert(unsaved: AddressRowUnsaved): ZIO[ZConnection, Throwable, AddressRow]
/* NOTE: this functionality requires PostgreSQL 16 or later! */
def insertUnsavedStreaming(unsaved: ZStream[ZConnection, Throwable, AddressRowUnsaved], batchSize: Int): ZIO[ZConnection, Throwable, Long]
def select: SelectBuilder[AddressFields, AddressRow]
def selectAll: ZStream[ZConnection, Throwable, AddressRow]
def selectById(addressid: AddressId): ZIO[ZConnection, Throwable, Option[AddressRow]]
def selectByIds(addressids: Array[AddressId]): ZStream[ZConnection, Throwable, AddressRow]
def update(row: AddressRow): ZIO[ZConnection, Throwable, Boolean]
def update: UpdateBuilder[AddressFields, AddressRow]
def upsert(unsaved: AddressRow): ZIO[ZConnection, Throwable, UpdateResult[AddressRow]]

Notice how the signatures use ZIO, ZStream, ZConnection.

zio-schema is not used

We opted to not go through zio-schema for the generated code. It was not clear that it was possible to implement all PostgreSQL features through zio-schema, and we wanted to generate code which is as fast to compile as possible.

Also support for zio-json

Typo supports generating JSON codecs for all the row types.. The PR also adds support for zio-json, so you can get codecs like this:

object AddressRow {
implicit lazy val jsonDecoder: JsonDecoder[AddressRow] = ???
implicit lazy val jsonEncoder: JsonEncoder[AddressRow] = ???