Dive Into ZIO Config
Note that this documentation is for 1.x series. For newer versions, please refer to docs section in GitHub.
Describe the config by handβ
We must fetch the configuration from the environment to a case class (product) in scala. Let it be MyConfig
import zio.IO
import zio.config._
import zio.ConfigProvider
import zio.Config, Config._
case class MyConfig(ldap: String, port: Int, dburl: String)
To perform any action using zio-config, we need a configuration description. Let's define a simple one.
val myConfig: Config[MyConfig] =
(string("LDAP") zip int("PORT") zip string("DB_URL")).to[MyConfig]
// Config[MyConfig]
To get a tuple,
val myConfigTupled: Config[(String, Int, String)] =
(string("LDAP") zip int("PORT") zip string("DB_URL"))
Fully automated Config Descriptionβ
If you don't like describing your configuration manually, and rely on the names of the parameter in the case class (or sealed trait),
there is a separate module called zio-config-magnolia
.
Note: zio-config-shapeless
is an alternative to zio-config-magnolia
to support scala 2.11 projects.
It will be deprecated once we find users have moved on from scala 2.11.
import zio.config._
import zio.config.magnolia._
val myConfigAutomatic = deriveConfig[MyConfig]
myConfig
and myConfigAutomatic
are same description, and is of the same type.
Refer to API docs for more explanations on descriptor More examples on automatic derivation is in examples module of zio-config
Read config from various sourcesβ
There are more information on various sources in here.
Below given is a simple example.
val map =
Map(
"LDAP" -> "xyz",
"PORT" -> "8888",
"DB_URL" -> "postgres"
)
val source = ConfigProvider.fromMap(map)
source.load(myConfig)
Documentations using Configβ
generateDocs(myConfig)
//Creates documentation (automatic)
val betterConfig =
(string("LDAP") ?? "Related to auth" zip int("PORT") ?? "Database port" zip
string("DB_URL") ?? "url of database"
).to[MyConfig]
generateDocs(betterConfig).toTable.toGithubFlavouredMarkdown
// Custom documentation along with auto generated docs
Accumulating all errorsβ
For any misconfiguration, the ReadError collects all of them with proper semantics: AndErrors
and OrErrors
.
Instead of directly printing misconfigurations, the ReadError.prettyPrint
shows the path, detail of collected misconfigurations.
- All misconfigurations of
AndErrors
are put in parallel lines.
β₯
β βββ
β β FormatError
β MissingValue
OrErrors
are in the same line which indicates a sequential misconfiguration
β₯
β MissingValue
β
β FormatError
Here is a complete example:
ReadError:
β₯
β βββ¦βββ
β β β
β β β βMissingValue
β β β path: var2
β β β Details: value of type string
β β β
β β β βMissingValue path: envvar3
β β β path: var3
β β β Details: value of type string
β β β
β β βΌ
β β
β β βFormatError
β β cause: Provided value is wrong, expecting the type int
β β path: var1
β βΌ
βΌ
Example of mapping keysβ
Now on, the only way to change keys is as follows:
// mapKey is just a function in `Config` that pre-existed
val config = deriveConfig[Config].mapKey(_.toUpperCase)
Inbuilt support for pure-configβ
Many users make use of the label type
in HOCON files to annotate the type of the coproduct.
Now on, zio-config has inbuilt support for reading such a file/string using descriptorForPureConfig
.
import zio.config._, typesafe._, magnolia._
@nameWithLabel("type")
sealed trait X
case class A(name: String) extends X
case class B(age: Int) extends X
case class AppConfig(x: X)
val str =
s"""
x : {
type = A
name = jon
}
"""
ConfigProvider.fromHoconString(str).load(deriveConfig[AppConfig])
The to
method for easy manual configurationsβ
import zio.config._
import zio.Config
final case class AppConfig(port: Int, url: String)
val config = Config.int("PORT").zip(Config.string("URL")).to[AppConfig]
A few handy methodsβ
CollectAllβ
import zio.config._
final case class Variables(variable1: Int, variable2: Option[Int])
val listOfConfig: List[Config[Variables]] =
List("GROUP1", "GROUP2", "GROUP3", "GROUP4")
.map(group => (Config.int(s"${group}_VARIABLE1") zip Config.int(s"${group}_VARIABLE2").optional).to[Variables])
val configOfList: Config[List[Variables]] =
Config.collectAll(listOfConfig.head, listOfConfig.tail: _*)
orElseEither && Constantβ
import zio.config._
sealed trait Greeting
case object Hello extends Greeting
case object Bye extends Greeting
val configSource =
ConfigProvider.fromMap(Map("greeting" -> "Hello"))
val config: Config[Greeting] =
Config.constant("Hello").orElseEither(Config.constant("Bye")).map(_.merge)