A Solution to the Elm - Config Conundrum
When writing elm apps one often encounters the problem of how to get different configurations for different environments in the app. Often this is a base url for an API.
We are looking for a solution that supports all three different environments:
- Local Development (using elm-reactor)
This problem is known and was discussed before. Solutions include:
Adding a config-files outside of git, using a reverse proxy, serving the config as json file, passing it as flag or using string replacements. Link to Discussion
Another proposal is to use elm-app and environment variables. Link
All these solutions either lack the simplicity of just having a constant string in your code, or miss the point that configurations should be part of the git repository.
Looking at elm-make for a solution
elm-make --help responds with:
Usage: elm-make [FILES...] [--output FILE] [--yes] [--report FORMAT] [--debug] [--warn] [--docs FILE] [--prepublish] [--prepublish-core]
So, how about building the app with
elm-make src/Main.elm env/test/Env.elm --output target/test/elm.js
Env.elm could look like this:
module Env exposing (apiUrl) apiUrl : String -> String apiUrl str = "https://test.api-example.com/api" ++ str
Inside the app we can refer to it like this:
import Env exposing (apiUrl)
This allows us to compile
Main.elm against different
What about elm-reactor
Elm-reactor has no arguments for extra files so the previous solution will not work.
It will tell us:
I cannot find module 'Env'. Module 'Request is trying to import it. Potential problems could be: * Misspelled the module name * Need to add a source directory or new dependency to elm-package.json
This can be solved by putting the
Env.elm which should be used in local development into the
It acts as a fallback as
elm-make seems to prefer input files over files in the source directory.
Putting it all together
We have an
Env.elm for every environment we want to build against:
. ├── env │ ├── prod │ │ └── Env.elm │ └── test │ └── Env.elm └── src ├── Env.elm └── Main.elm
We also have a
Env.elm lying in the
src/ folder to be used by elm-reactor.
elm-package.json contains the
src/ directory but not the
"source-directories": [ "src" ],
And now, to make things more convenient: Let’s create a good old Makefile!
TARGET = target/js all: test live test: rm -f elm-stuff/build-artifacts/0.18.0/user/project/1.0.0/Env.elm* elm-make src/Main.elm env/test/Env.elm --output $(TARGET)/test/elm.min.js elm-make src/Main.elm env/test/Env.elm --output $(TARGET)/test/elm.debug.min.js --debug rm -f elm-stuff/build-artifacts/0.18.0/user/project/1.0.0/Env.elm* live: rm -f elm-stuff/build-artifacts/0.18.0/user/project/1.0.0/Env.elm* elm-make src/Main.elm env/prod/Env.elm --output $(TARGET)/prod/elm.min.js elm-make src/Main.elm env/prod/Env.elm --output $(TARGET)/prod/elm.debug.min.js --debug rm -f elm-stuff/build-artifacts/0.18.0/user/project/1.0.0/Env.elm*
rm -f call to
elm-stuff you ask?
It seems elm-make tries to use a cached and previously compiled artefact of
Env.elm if it is available.
So before and after every build we delete it.
This solution works for all environments without manual adaptions. The Makefile can be used by your build scripts. Local development works as good as ever and changing and distributing configurations is trivial.