DEV Community

Cover image for Fauda: configuration made simple
Nicolas Gryman
Nicolas Gryman

Posted on

Fauda: configuration made simple

So, you finally wrapped up the core features of your new app / CLI tool. Great!

On with the configuration part...

Which file format to support? How to load these files? How to load env vars and CLI options as well? How to merge everything together? How to validate options and apply default values? Should Typescript be supported? Etc...

Chances are you have deadlines, so configuration might not be your top priority. As a consequence, you only have a limited time to address this. Even though you can find awesome libraries to help you implement each piece independently, you still need to figure out all the plumbing and handle every edge case. This could quickly become painful and time-consuming.

If this rings a bell, then you might be interested in Fauda!


It's an all-in-one library that:

  1. loads options from multiple sources: env vars, CLI options, and configuration files.
  2. merges them together in one unified configuration object.
  3. normalizes it by validating against a JSON schema and setting default values.

It offers the following advantages:

  • Simple - a single dependency to load, merge, and validate your configuration.
  • Flexible - multiple file formats support out of the box such as JSON, YAML, JavaScript, and even Typescript!
  • Reliable - a unique source of truth defined in a JSON schema.
  • Typescript friendly - generated typings for your code and configuration files (bonus: auto-completion in VSCode). Take a look at https://github.com/ngryman/fauda for more info. Any feedback would be very much appreciated!

Getting Started

Let's assume you want to configure a server application with the following options:

  • port: The port the server listens to.
  • open: Open in a browser tab if true.
  • mode: Mode of the app.
  • publicPages: A list of public pages.

Install Fauda

npm install fauda
Enter fullscreen mode Exit fullscreen mode

Set up your JSON schema

Fauda uses a JSON schema to load and normalize your configuration.

Create a schema.json file:

{
  "$schema": "http://json-schema.org/draft-07/schema",
  "title": "My awesome app configuration",
  "type": "object",
  "properties": {
    "$schema": {
      "description": "Path to my app's schema.",
      "type": "string"
    },
    "port": {
      "description": "The port the server listens to.",
      "type": "number",
      "default": 3000
    },
    "open": {
      "description": "Open in a browser tab if true.",
      "type": "boolean",
      "default": false
    },
    "mode": {
      "description": "Mode of the app.",
      "type": "string",
      "enum": ["development", "production"],
      "default": "${NODE_ENV}"
    },
    "publicPages": {
      "description": "A list of public pages.",
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  },
  "required": ["publicPages"]
}
Enter fullscreen mode Exit fullscreen mode

For more information on JSON schemas, you can take a look at their Getting Started guide.

Generate types (optional)

Generating types allows you to have a strongly typed configuration object in your code. As a bonus, it also enables autocompletion for Typescript configuration files!

Generate a src/configuration.ts file:

$ npx fauda types
Enter fullscreen mode Exit fullscreen mode

This will generate the following file:

export interface Configuration {
  port?: number
  open?: boolean
  mode?: 'development' | 'production'
  publicPages: string[]
}
Enter fullscreen mode Exit fullscreen mode

For more information about generating types, please take a look at the README's CLI section.

Load & validate your configuration

Assuming your package's name is my-app:

import { fauda } from 'fauda'
import { Configuration } from './configuration'

async function loadConfiguration() {
  try {
    const configuration = await fauda<Configuration>('my-app')
  } catch (err) {
    console.error(err.message)
  }
}
Enter fullscreen mode Exit fullscreen mode

That's all folks! 🎉

What's next?

You can take a look at Fauda's README, it's still in early development so any feedback will be much appreciated!

Top comments (7)

Collapse
 
zomglings profile image
Neeraj Kashyap

This looks like a well-written tool, but I would avoid using it over the concern that it adds too many levels of indirection in my code.

Say I am writing a command line tool. If I'm doing this in Javascript, I usually use yargs. Either I would have to duplicate the contents of my Fauda config definition JSON schema, or I would have to write code which mapped over the JSON schema object to programmatically configure my yargs parser.

Also the fact that configuration can come from so many different sources -- environment variables, command line arguments, and configuration files -- would make it really difficult to debug a server application that used Fauda to manage its configuration. When I SSH into the server or exec into the container running my application, I like to be able to do a simple

env | grep "MY_APP_"
Enter fullscreen mode Exit fullscreen mode

to see what configuration the application is running with.

Just some feedback based on previous times I've been bitten by similar tools (dotenv in Javascript land, Viper in Go land).

I will say that the generation of typescript types from Fauda configuration is a really nice idea.

Collapse
 
ngryman profile image
Nicolas Gryman • Edited

Thanks for the feedback Neeraj!

I think this is perfectly fine to stick with yargs and manage configuration manually when it makes sense.

One of Fauda's objectives is to help tools targeting a broad audience with diverse needs and technical backgrounds.

From my experience writing internal tooling at my company, as the audience grows, so do the use cases. Folks come up with various ways of integrating a tool in their workflow. Depending on their tech stack, personal preferences, and technical constraints, they tend to need more than one configuration source simultaneously.

Fauda helps to support all of these use cases out of the box, without spending too much time figuring out the specifics.


I would be interested to understand your use case better:

Say I am writing a command-line tool. If I'm doing this in Javascript, I usually use yargs. Either I would have to duplicate the contents of my Fauda config definition JSON schema [...]

Fauda already maps CLI options based on your JSON schema. For instance, a mode option would be mapped to the --mode CLI option. So, in theory, you shouldn't need yargs to parse these options. Are there other reasons you would need yargs?

Also, the fact that configuration can come from so many different sources -- environment variables, command-line arguments, and configuration files -- would make it really difficult to debug a server application that used Fauda to manage its configuration.

That's a good point. I think this is inherent to having multiple sources of configuration. Possibly Fauda could come up with a solution. I would need to think about it a bit more, but I think a debug mode that displays the currently loaded configuration information could help.

For instance, it could look like something like this:

$ DEBUG=1 MY_APP_MODE=development my-app --port=3000

Option    Value              Sources
--------------------------------------
mode      development        ENV*, FILE
open      false              FILE*
port      3000               CLI*, FILE
Enter fullscreen mode Exit fullscreen mode

The * would indicate the currently used source.

Let me know your thoughts about this and if you think it could help debugging.

Collapse
 
zomglings profile image
Neeraj Kashyap

I haven't used yargs in a long time simply because our backend code is written in Python and Go has become my language of choice for one-off CLI tools. From that point of view, take whatever I say with a grain of salt because my language choices mean I am not a Fauda user by default.

That said, I have spent a lot of time in my life giving devops support to development teams that developed entirely in javascript. These are the experiences that made me wary of libraries like dotenv.

Even in javascript, i really dislike yargs - it just so happens that that's the library that I'm (unfortunately) most familiar with. Would be happy to switch to something like Fauda.

Your idea of a debug mode is fantastic. Some suggestions of different flavors:

  1. It is valuable to know how a given invocation of my-app is configured, complete with values from the command line. Maybe a command like fauda inspect <my-app invocation taken from output of ps command>?

  2. If a config value came from a file, would be nice to know the file path.

  3. If a config value came from an environment variable, would be nice to know the env var name.

  4. If a config value came from the CLI, maybe a snippet of the invocation with the argument highlighted?

To build sympathy for this use case, imagine you know almost nothing about the application. You are trying to debug some error message from your production logs by shelling into the server/container running that application. Once you are in, you want to know as quickly as possible:

  1. how the application was invoked

  2. what parameters it is using

If the application is using Fauda and I can use Fauda to get this information easily, I would forever recommend it to all JS developers I know.

Thread Thread
 
ngryman profile image
Nicolas Gryman • Edited

Thanks for the feedback!

I like the idea of having a fauda inspect. It could be a non-intrusive way to debug an already running Fauda-configured app.

I believe this could end up in unpredictable results though. The configuration file or the environment variables might have changed since the app has started, and fauda inspect could return values that don't reflect the startup app's configuration.

I think this could work if we enabled some communication between a Fauda-configured app and fauda inspect (e.g. socket). However, this would quickly add complexity to the current implementation. 😅

Do you see any other approaches that could allow fauda inspect to inspect an already running app?

Thread Thread
 
zomglings profile image
Neeraj Kashyap

You know, I need to retract my previous statement about Viper (github.com/spf13/viper). Used it this weekend to build a command line tool in Go, and I found it to be quite useful.

This is the tool: github.com/bugout-dev/bugout-go

When someone installs that tool, they get access to a bugout binary. They can initialize a config file using bugout state init. The approach I've taken to inspecting state is bugout state current. This shows the config file path as well as the current config key-value pairs.

It's nowhere near as comprehensive as what Fauda allows you to do, but it suggests a different approach than a live Fauda socket (which, as you rightly pointed out, is way too much complexity). Instead, you can just write (atomically) Fauda state into a tempfile and read the most recent state from that file on fauda inspect.

Collapse
 
shriji profile image
Shriji

Interesting!

Why did you choose Fauda as the name?

Collapse
 
ngryman profile image
Nicolas Gryman

I really like the word's sound, and I knew that Fauda means "chaos" in Hebrew and Arabic. I found it interesting to use for a project that tries to "sort out chaos" by loading your configuration from various sources.