{"id":13513867,"url":"https://github.com/elixir-toniq/vapor","last_synced_at":"2025-12-12T00:30:32.961Z","repository":{"id":40267708,"uuid":"135068644","full_name":"elixir-toniq/vapor","owner":"elixir-toniq","description":"Runtime configuration system for Elixir","archived":false,"fork":false,"pushed_at":"2023-04-10T11:02:31.000Z","size":307,"stargazers_count":594,"open_issues_count":13,"forks_count":37,"subscribers_count":9,"default_branch":"main","last_synced_at":"2025-03-28T13:08:00.351Z","etag":null,"topics":["configuration","elixir"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/elixir-toniq.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-05-27T17:46:13.000Z","updated_at":"2025-03-17T17:42:52.000Z","dependencies_parsed_at":"2024-01-13T19:24:14.781Z","dependency_job_id":"1ec7bfbf-adec-4a5e-a0d0-664b0551ed88","html_url":"https://github.com/elixir-toniq/vapor","commit_stats":null,"previous_names":["keathley/vapor"],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-toniq%2Fvapor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-toniq%2Fvapor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-toniq%2Fvapor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-toniq%2Fvapor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/elixir-toniq","download_url":"https://codeload.github.com/elixir-toniq/vapor/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247190250,"owners_count":20898702,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["configuration","elixir"],"created_at":"2024-08-01T05:00:39.114Z","updated_at":"2025-12-12T00:30:32.920Z","avatar_url":"https://github.com/elixir-toniq.png","language":"Elixir","funding_links":["https://github.com/sponsors/keathley_"],"categories":["Elixir"],"sub_categories":[],"readme":"# Vapor\n\n[![Elixir CI](https://github.com/keathley/vapor/actions/workflows/elixir.yml/badge.svg)](https://github.com/keathley/vapor/actions/workflows/elixir.yml)\n[![Module Version](https://img.shields.io/hexpm/v/vapor.svg)](https://hex.pm/packages/vapor)\n[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/vapor/)\n[![Total Download](https://img.shields.io/hexpm/dt/vapor.svg)](https://hex.pm/packages/vapor)\n[![License](https://img.shields.io/hexpm/l/vapor.svg)](https://github.com/keathley/vapor/blob/master/LICENSE)\n[![Last Updated](https://img.shields.io/github/last-commit/keathley/vapor.svg)](https://github.com/keathley/vapor/commits/master)\n\n\u003c!-- MDOC !--\u003e\n\nLoads dynamic configuration at runtime.\n\n## Sponsors\n\nThis project is sponsored by these fine folks:\n\n\u003cimg alt=\"smartlogic\" height=\"30px\" src=\"assets/smartlogic.png?raw=true\"\u003e\n\n_If you're interested in sponsoring you can do so here: https://github.com/sponsors/keathley_\n\n## Why Vapor?\n\nDynamically configuring elixir apps can be hard. There are major\ndifferences between configuring applications with mix and configuring\napplications in a release. Vapor wants to make all of that easy by\nproviding an alternative to mix config for runtime configs. Specifically Vapor can:\n\n  * Find and load configuration from files (JSON, YAML, TOML).\n  * Read configuration from environment variables.\n  * `.env` file support for easy local development.\n\n## Example\n\n```elixir\ndefmodule VaporExample.Application do\n  use Application\n  alias Vapor.Provider.{File, Env}\n\n  def start(_type, _args) do\n    providers = [\n      %Env{bindings: [db_url: \"DB_URL\", db_name: \"DB_NAME\", port: \"PORT\"]},\n      %File{path: \"config.toml\", bindings: [kafka_brokers: \"kafka.brokers\"]},\n    ]\n\n    # If values could not be found we raise an exception and halt the boot\n    # process\n    config = Vapor.load!(providers)\n\n    children = [\n       {VaporExampleWeb.Endpoint, port: config.port},\n       {VaporExample.Repo, [db_url: config.db_url, db_name: config.db_name]},\n       {VaporExample.Kafka, brokers: config.kafka_brokers},\n    ]\n\n    opts = [strategy: :one_for_one, name: VaporExample.Supervisor]\n    Supervisor.start_link(children, opts)\n  end\nend\n```\n\n### Precedence\n\nVapor merges the configuration based on the order that the providers are specified.\n\n```elixir\nproviders = [\n  %Dotenv{},\n  %File{path: \"$HOME/.vapor/config.json\", bindings: []},\n  %Env{bindings: []},\n]\n```\n\nEnv will have the highest precedence, followed by File, and finally Dotenv.\n\n### Reading config files\n\nConfig files can be read from a number of different file types including\nJSON, TOML, and YAML. Vapor determines which file format to use based on the file extension.\n\n### Options on bindings\n\nBindings for `%Env{}` and `%File{}` providers support a number of options:\n\n* `:map` - Allows you to pass a \"translation\" function with the binding.\n* `:default` - If the value is not found then the default value will be returned instead. Defaults always skip the translations.\n* `: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.\n\n```elixir\nproviders = [\n  %Env{\n    bindings: [\n      {:db_name, \"DB_NAME\"},\n      {:db_port, \"DB_PORT\", default: 4369, map: \u0026String.to_integer/1},\n    ]\n  }\n]\n```\n\n## Adding configuration plans to modules\n\nVapor provides a `Vapor.Plan` behaviour. This allows modules to describe a provider\nor set of providers.\n\n```elixir\ndefmodule VaporExample.Kafka do\n  @behaviour Vapor.Plan\n\n  @impl Vapor.Plan\n  def config_plan do\n    %Vapor.Provider.Env{\n      bindings: [\n        {:brokers, \"KAFKA_BROKERS\"},\n        {:group_id, \"KAFKA_CONSUMER_GROUP_ID\"},\n      ]\n    }\n  end\nend\n\nconfig = Vapor.load!(VaporExample.Kafka)\n```\n\n## Planner DSL\n\nWhile using the structs directly is a perfectly reasonable option, it can often\nbe verbose. Vapor provides a DSL for specifying configuration plans using less\nlines of code.\n\n```elixir\ndefmodule VaporExample.Config do\n  use Vapor.Planner\n\n  dotenv()\n\n  config :db, env([\n    {:url, \"DB_URL\"},\n    {:name, \"DB_NAME\"},\n    {:pool_size, \"DB_POOL_SIZE\", default: 10, map: \u0026String.to_integer/1},\n  ])\n\n  config :web, env([\n    {:port, \"PORT\", map: \u0026String.to_integer/1},\n  ])\n\n  config :kafka, VaporExample.Kafka\nend\n\ndefmodule VaporExample.Application do\n  use Application\n\n  def start(_type, _args) do\n    config = Vapor.load!(VaporExample.Config)\n\n    children = [\n       {VaporExampleWeb.Endpoint, config.web},\n       {VaporExample.Repo, config.db},\n       {VaporExample.Kafka, config.kafka},\n    ]\n\n    opts = [strategy: :one_for_one, name: VaporExample.Supervisor]\n    Supervisor.start_link(children, opts)\n  end\nend\n```\n\n## Custom Providers\n\nThere are several built in providers\n\n - Environment\n - .env files\n - JSON\n - YAML\n - TOML\n\nIf you need to create a new provider you can do so with the included\n`Vapor.Provider` protocol.\n\n```elixir\ndefmodule MyApp.DatabaseProvider do\n  defstruct [id: nil]\n\n  defimpl Vapor.Provider do\n    def load(db_provider) do\n    end\n  end\nend\n```\n\n\u003c!-- MDOC !--\u003e\n\n## Why does this exist?\n\nWhile its possible to use Elixir's release configuration for some use cases,\nrelease configuration has some issues:\n\n* If configuration ends up in Application config then its still functioning as a global and is shared across all of your running applications.\n* Limited ability to recover from failures while fetching config from external providers.\n* Its difficult to layer configuration from different sources.\n\nVapor is designed to solve these problems.\n\n## Installing\n\nAdd vapor to your mix dependencies:\n\n```elixir\ndef deps do\n  [\n    {:vapor, \"~\u003e 0.10\"},\n  ]\nend\n```\n\n## Resources from the community\n\n[Configuring your Elixir Application at Runtime with Vapor](https://blog.appsignal.com/2020/04/28/configuring-your-elixir-application-at-runtime-with-vapor.html)\n\n## Copyright and License\n\nCopyright (c) 2020 Christopher Keathley\n\nThis library is released under the MIT License. See the [LICENSE.md](./LICENSE.md) file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felixir-toniq%2Fvapor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felixir-toniq%2Fvapor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felixir-toniq%2Fvapor/lists"}