Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/elixir-toniq/vapor
Runtime configuration system for Elixir
https://github.com/elixir-toniq/vapor
configuration elixir
Last synced: 1 day ago
JSON representation
Runtime configuration system for Elixir
- Host: GitHub
- URL: https://github.com/elixir-toniq/vapor
- Owner: elixir-toniq
- License: mit
- Created: 2018-05-27T17:46:13.000Z (over 6 years ago)
- Default Branch: main
- Last Pushed: 2023-04-10T11:02:31.000Z (almost 2 years ago)
- Last Synced: 2025-01-19T00:37:08.882Z (9 days ago)
- Topics: configuration, elixir
- Language: Elixir
- Homepage:
- Size: 300 KB
- Stars: 594
- Watchers: 10
- Forks: 38
- Open Issues: 13
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.md
Awesome Lists containing this project
README
# Vapor
[![Elixir CI](https://github.com/keathley/vapor/actions/workflows/elixir.yml/badge.svg)](https://github.com/keathley/vapor/actions/workflows/elixir.yml)
[![Module Version](https://img.shields.io/hexpm/v/vapor.svg)](https://hex.pm/packages/vapor)
[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/vapor/)
[![Total Download](https://img.shields.io/hexpm/dt/vapor.svg)](https://hex.pm/packages/vapor)
[![License](https://img.shields.io/hexpm/l/vapor.svg)](https://github.com/keathley/vapor/blob/master/LICENSE)
[![Last Updated](https://img.shields.io/github/last-commit/keathley/vapor.svg)](https://github.com/keathley/vapor/commits/master)Loads dynamic configuration at runtime.
## Sponsors
This project is sponsored by these fine folks:
_If you're interested in sponsoring you can do so here: https://github.com/sponsors/keathley_
## Why Vapor?
Dynamically configuring elixir apps can be hard. There are major
differences between configuring applications with mix and configuring
applications in a release. Vapor wants to make all of that easy by
providing an alternative to mix config for runtime configs. Specifically Vapor can:* Find and load configuration from files (JSON, YAML, TOML).
* Read configuration from environment variables.
* `.env` file support for easy local development.## Example
```elixir
defmodule VaporExample.Application do
use Application
alias Vapor.Provider.{File, Env}def start(_type, _args) do
providers = [
%Env{bindings: [db_url: "DB_URL", db_name: "DB_NAME", port: "PORT"]},
%File{path: "config.toml", bindings: [kafka_brokers: "kafka.brokers"]},
]# If values could not be found we raise an exception and halt the boot
# process
config = Vapor.load!(providers)children = [
{VaporExampleWeb.Endpoint, port: config.port},
{VaporExample.Repo, [db_url: config.db_url, db_name: config.db_name]},
{VaporExample.Kafka, brokers: config.kafka_brokers},
]opts = [strategy: :one_for_one, name: VaporExample.Supervisor]
Supervisor.start_link(children, opts)
end
end
```### Precedence
Vapor merges the configuration based on the order that the providers are specified.
```elixir
providers = [
%Dotenv{},
%File{path: "$HOME/.vapor/config.json", bindings: []},
%Env{bindings: []},
]
```Env will have the highest precedence, followed by File, and finally Dotenv.
### Reading config files
Config files can be read from a number of different file types including
JSON, TOML, and YAML. Vapor determines which file format to use based on the file extension.### Options on bindings
Bindings for `%Env{}` and `%File{}` providers support a number of options:
* `:map` - Allows you to pass a "translation" function with the binding.
* `:default` - If the value is not found then the default value will be returned instead. Defaults always skip the translations.
* `:required` - Marks the binding a required or not required (defaults to true). If required values are missing, and there is no default present, then the provider will return an exception. If the binding is marked `required: false`, then the provider returns the key with a `nil` value.```elixir
providers = [
%Env{
bindings: [
{:db_name, "DB_NAME"},
{:db_port, "DB_PORT", default: 4369, map: &String.to_integer/1},
]
}
]
```## Adding configuration plans to modules
Vapor provides a `Vapor.Plan` behaviour. This allows modules to describe a provider
or set of providers.```elixir
defmodule VaporExample.Kafka do
@behaviour Vapor.Plan@impl Vapor.Plan
def config_plan do
%Vapor.Provider.Env{
bindings: [
{:brokers, "KAFKA_BROKERS"},
{:group_id, "KAFKA_CONSUMER_GROUP_ID"},
]
}
end
endconfig = Vapor.load!(VaporExample.Kafka)
```## Planner DSL
While using the structs directly is a perfectly reasonable option, it can often
be verbose. Vapor provides a DSL for specifying configuration plans using less
lines of code.```elixir
defmodule VaporExample.Config do
use Vapor.Plannerdotenv()
config :db, env([
{:url, "DB_URL"},
{:name, "DB_NAME"},
{:pool_size, "DB_POOL_SIZE", default: 10, map: &String.to_integer/1},
])config :web, env([
{:port, "PORT", map: &String.to_integer/1},
])config :kafka, VaporExample.Kafka
enddefmodule VaporExample.Application do
use Applicationdef start(_type, _args) do
config = Vapor.load!(VaporExample.Config)children = [
{VaporExampleWeb.Endpoint, config.web},
{VaporExample.Repo, config.db},
{VaporExample.Kafka, config.kafka},
]opts = [strategy: :one_for_one, name: VaporExample.Supervisor]
Supervisor.start_link(children, opts)
end
end
```## Custom Providers
There are several built in providers
- Environment
- .env files
- JSON
- YAML
- TOMLIf you need to create a new provider you can do so with the included
`Vapor.Provider` protocol.```elixir
defmodule MyApp.DatabaseProvider do
defstruct [id: nil]defimpl Vapor.Provider do
def load(db_provider) do
end
end
end
```## Why does this exist?
While its possible to use Elixir's release configuration for some use cases,
release configuration has some issues:* If configuration ends up in Application config then its still functioning as a global and is shared across all of your running applications.
* Limited ability to recover from failures while fetching config from external providers.
* Its difficult to layer configuration from different sources.Vapor is designed to solve these problems.
## Installing
Add vapor to your mix dependencies:
```elixir
def deps do
[
{:vapor, "~> 0.10"},
]
end
```## Resources from the community
[Configuring your Elixir Application at Runtime with Vapor](https://blog.appsignal.com/2020/04/28/configuring-your-elixir-application-at-runtime-with-vapor.html)
## Copyright and License
Copyright (c) 2020 Christopher Keathley
This library is released under the MIT License. See the [LICENSE.md](./LICENSE.md) file.