Skip to main content

Modular Building Blocks for Modern Scala Applications

The philosophy is simple: use what you need, nothing more. Each block is independently useful and designed to compose with other blocks or your existing code.

libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.56"
val jsonStr = alice.toJsonString // {"name":"Alice","age":30}

Get startedGitHub

Works with ZIO 2.x · Cats Effect 3.x · Kyo · Ox · Akka · Plain Scala

01   Principles

Use What You Need, Nothing More

  1. 01 Zero Lock-In No dependency on ZIO, Cats Effect, or any other effect system.
  2. 02 Modular Each block is a separate artifact.
  3. 03 Cross-Platform Most blocks cross-build for JVM and Scala.js on Scala 2.13 and 3.x. Adopt Scala 3 on your timeline.
  4. 04 High Performance Implementations that avoid boxing, minimize allocations, and use platform-specific features where they pay off.
  5. 05 Type Safety Scala's type system carries the correctness guarantees, without runtime overhead.

02   Deep Dives

Four Blocks, in Code

Schema

The Schema block brings dynamic-language productivity to statically-typed Scala. Define your data types once, and derive codecs, validators, optics, and more automatically.

The Problem

In statically-typed languages, you often maintain separate codec implementations for each data format (JSON, Avro, Protobuf, etc.). Meanwhile, dynamic languages handle data effortlessly:

// JavaScript: one line and done
const data = await res.json();

The Solution

ZIO Blocks Schema derives everything from a single schema definition:

case class Person(name: String, age: Int)
object Person {
implicit val schema: Schema[Person] = Schema.derived
}
// Derive codecs for any format:
val jsonCodec = Schema[Person].derive(JsonFormat) // JSON
val avroCodec = Schema[Person].derive(AvroFormat) // Avro
val toonCodec = Schema[Person].derive(ToonFormat) // TOON (LLM-optimized)
val msgpackCodec = Schema[Person].derive(MessagePackFormat) // MessagePack
val thriftCodec = Schema[Person].derive(ThriftFormat) // Thrift

One Schema, Many Formats

  • JSON
  • Avro
  • BSON
  • CSV
  • MessagePack
  • Thrift
  • TOON
  • XML
  • YAML

Scope

Compile-time verified resource safety for synchronous Scala code. Scope prevents resource leaks at compile time by tagging values with an unnameable type-level identity—values allocated in a scope can only be used within that scope. Child scope values cannot escape to parent scopes, enforced by both the abstract scope-tagged type and the Unscoped constraint on scoped.

The Problem

Resource management in Scala is error-prone:

// Classic try/finally - verbose and easy to get wrong
val db = openDatabase()
try {
val tx = db.beginTransaction()
try {
doWork(tx)
tx.commit()
} finally tx.close() // What if commit() throws?
} finally db.close()
// Using - better, but doesn't prevent returning resources
Using(openDatabase()) { db =>
db // Oops! Returned the resource - use after close!
}

The Solution

Scope makes resource leaks a compile error, not a runtime bug:

import zio.blocks.scope.*
Scope.global.scoped { scope =>
import scope.*
val db: $[Database] = allocate(Resource(openDatabase()))
// Methods are hidden - can't call db.query() directly
// Must use $ to access:
val result: String = $(db)(_.query("SELECT 1"))
// Trying to return `db` would be a compile error!
result // Only pure data (String) escapes
}
// db.close() called automatically

Async

A lightweight, zero-dependency asynchronous effect type. A ready Async[A] is an A, so synchronous code composed with map / flatMap allocates nothing on the happy path while still suspending on genuinely asynchronous work.

The Problem

Asynchronous Scala forces a choice between two costs. Future allocates for every combinator and needs an ExecutionContext threaded everywhere, even when the value is already available. Full effect systems avoid that but ask you to adopt a runtime, a set of type classes, and a programming model across your whole codebase—a heavy price for a library that only occasionally suspends.

The Solution

Async[A] is a value, not a wrapper. When the result is already known, the representation is the result, so composing ready values costs nothing:

import zio.blocks.async._
// Constructors collapse to bare values; transformers inline with no allocation
val computed: Int =
Async.succeed(20).map(_ + 1).flatMap(n => Async.succeed(n * 2)).block
  • Getting Started with Async — create, compose, and run async effects
  • Async reference — the full API, including zip, catchAll, collectAll, the Async.promise callback bridge, and Future / CompletionStage interop
  • async-examples — a single-file order-fulfillment demo (cd async-examples && sbt run)

SQL

A thin, type-safe JDBC wrapper that maps Scala case classes to database tables using the same Schema you use for JSON and Avro codecs. No ORM runtime, no code generation — just composable SQL fragments, a derived repository abstraction, and a direct ZIO integration.

The Problem

JDBC is powerful but tedious: manual ResultSet traversal, index-based parameter binding, and repetitive CRUD boilerplate make even simple database access error-prone. ORMs solve the boilerplate but add heavy runtimes, hidden queries, and opaque magic.

The Solution

ZIO Blocks SQL derives everything from a single Schema[A]:

case class User(id: Long, name: String, email: String)
object User:
given Schema[User] = Schema.derived
// Derive the table, codec, and repository in one line
val repo = Repo.derived[User, Long]
// Use the sql"..." interpolator for custom queries
val frag = sql"SELECT * FROM user WHERE email = ${"alice@example.com"}"
  • SQL reference — DbCodec, Frag, Table, Repo, Transactor, dialects, and DDL generation
  • Query DSL guide — a four-part series building a type-safe query language on reified optics

03   Block Catalog

Take Only What You Need

Each block is a separate artifact under dev.zio. Copy the artifact name, add it to your build, and use it.

Meta Programming

Codecs

JSON support is built into zio-blocks-schema; the modules below add further formats.

Resource Management

  • Scope

    Compile-time safe resource boundaries that keep values from escaping their lifetime

    zio-blocks-scope

    JVM · JS / Scala 2.13 · 3.x

    Learn More about Scope

Dependency Injection

Configuration

Web & HTTP

Data Types

Concurrency

Streaming

  • Streams

    Pull-based streaming with typed errors, zero boxing, and synchronous or asynchronous execution

    zio-blocks-streams

    JVM · JS / Scala 2.13 · 3.x

    Learn More about Streams

Telemetry

Persistence

Tooling & Codegen