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
AndErrorsare put in parallel lines.
β₯
β βββ
β β FormatError
β MissingValue
OrErrorsare 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)