npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

brek

v2.2.20

Published

Brek is a powerful yet simple configuration library for Node.js. It’s structured, typed, and designed for dynamic configuration loading, making it perfect for securely managing secrets (e.g., AWS Secrets Manager). Written in TypeScript for safety and ease

Downloads

2,060

Readme

brek

(formerly lambdaconf)

build status SemVer Conventional Commits Static Badge

Brek stands for Blocking Resolution of Environment Keys.

Brek is a powerful yet simple configuration library for Node.js. It’s structured, typed, and designed for dynamic configuration loading, making it perfect for securely managing secrets (e.g., AWS Secrets Manager). Written in TypeScript for safety and ease of use. Sponsored by Aeroview.

🔒 Out-of-the-box Typescript support

  • Turn your runtime errors into safer compile-time errors! Automatically generated Typescript type definition for configuration object
  • Any override must satisfy Partial<DefaultConfig> type, or it will throw a Typescript error

😃 Simple, easy-to-use, safe, and stable

  • All settings are in simple, logic free .json files.
  • Adds structure and organization to your configuration files
  • Easily see what is being overridden and where
  • Comprehensive yet easy to understand documentation
  • Small, modular, and unit-tested codebase written in Typescript with no dependencies.

💪 Flexible & powerful

  • Differentiates between concepts such as environment, deployment, and user and provides an out-of-the-box solution with sensible merge strategy
  • Provides for overrides via CLI without polluting the CLI argument namespace
  • Fast. Runtime processing is done during app initialization only.
  • Put environment variables directly into .json files

🤖 Dynamic loading

  • Great for AWS Secrets Manager, AWS Parameter Store, HashiCorp Vault, or custom dynamic runtime functions
  • Any custom logic lives in loaders, keeping your config files logic-free
  • Provides an easy sharable and reusable plugin interface for sharing or re-use

Installation & Setup

1. Install from npm

npm i brek

2. Create conf directory

Create a directory called conf in the root of your project. This is where your configuration will go, along with the generated Conf.d.ts TypeScript Declaration File.

Note: If you want to use a different directory, you can set the BREK_CONF_DIR environment variable to the path of your configuration directory.

Here's an example conf folder:

root/
└── conf/
    └── deployments
        └── test.acme.json
    └── environments
        └── development.json
        └── production.json
    └── users
        └── john.json
    └── default.json

At a minimum, default.json is required at the root of your conf folder. To learn more about the other folders, see merge strategy and configuration rules

3. Create your configuration files

Here's a simple example:

default.json

{
  "postgres": {
    "host": "localhost",
    "port": 5432,
    "user": "pguser"
  }
  "port": 3000,
  "foo": {
    "bar": true
  }
}

At a minimum, default.json is required at the root of your conf folder.

See full configuration rules, merge strategy, and reference the example folder structure above. Also, don't forget to check out loaders for dynamic runtime configuration.

4. Typescript Configuration (tsconfig.json)

Make sure the generated conf/Conf.d.ts file will be picked up by your Typescript parser. One way to do this is by including it in your include directive like so:

  "include":[
    "src/**/*",
    "conf/Conf.d.ts"
  ],

If you're using ts-node, it might help to add the following:

"ts-node": {
  "files": true
}

5. Call brek when your configuration changes to generate the type declaration file

Whenever your default.json configuration changes, you'll need to run the brek command to build the type declaration file. For example, you could add the following to your package.json file:

{
  "scripts": {
    "postinstall": "brek"
  }
}

To run this manually, you can run npx brek. This will generate the Conf.d.ts file in your conf folder.

6. Optional: Add generated files to .gitignore

You may want to add conf/Conf.d.ts and conf/conf.json to your .gitignore file to prevent them from being checked into source control.

Loading the configuration

Before you can read the configuration within your app, you must first load it. This step involves reading the files from disk, merging them, and resolving any loaders. You have two options:

  1. Use loadConf() within your app to load the configuration asynchronously before your app starts.

  2. Use loadConf() in a script to generate the conf.json configuration file before running your app.

Here's an example of using loadConf() in your app:

import {loadConf, getConf} from "brek";

loadConf() // optionally pass in loaders here
    .then(() => {
        const conf = getConf();
        console.log(conf);
        // start your server, etc.
    })
    .catch(console.log.bind(console));

Here's an example of using loadConf() in an init script:

import {loadConf} from "brek";

loadConf()  // optionally pass in loaders here
    .then(() => {
        console.log("Configuration loaded successfully and written to conf.json");
    })
    .catch(console.log.bind(console));
ts-node init.ts && ts-node src/index.ts

Getting the config object

Once loaded, use getConf to access the configuration object. The configuration is cached after the first load, so you can call getConf as many times as you want without worrying about performance.

Example:

import {getConf} from "brek";

const conf = getConf(); // type of Conf

console.log(conf); // logs config object

const isFooBarEnabled: boolean = conf.foo.bar; // will throw Typescript error as expected if does not exist or is not a boolean

If you need the type interface, you can import it:

import {Conf} from "brek";

Configuration rules

  • default.json is required, everything else is optional. Recommended practice is that default.json contains all of your "local development" settings.

  • All configuration files must be a subset of default.json. Think of them simply as overrides to the default. In Typescript terms, conf files must be of type Partial<Conf>.

  • A property's type should not change simply because of a different environment, user, or deployment. This is basically saying the same as above.

  • Loaders always must return a string. If you need to return a different type, you can use JSON.parse() or similar.

  • Arrays should be homogenous (not of mixed types).

Configuration merge strategy

Configurations are merged in this order, with the later ones overriding the earlier ones:

  1. default.json
  2. environment file
  3. deployment file
  4. user file
  5. CLI/env overrides

Which of these sources to choose depends on the presence of certain process.env configuration variables:

| process.env | conf file | | ----------------------- | --------------------------------------| | NODE_ENV | /conf/environments/[NODE_ENV].json | | DEPLOYMENT | /conf/deployments/[DEPLOYMENT].json | | USER | /conf/users/[USER].json | | BREK | N/A | | OVERRIDE (deprecated) | N/A |

A few notes:

  • BREK must be valid JSON. Learn more
  • USER is usually provided by default by UNIX environments (try console.log(process.env.USER))
  • Loaders parameters are simply replaced, not merged. A loader instance is treated as a primitive.
  • Arrays are simply replaced, not merged.

Using CLI/env overrides

You can use the BREK environment variable to override properties via CLI/ENV. BREK must be valid JSON. Using jq simplifies dynamic JSON construction, ensures proper quoting, and makes it easier to handle environment variables.

Examples

# Override the a.b property
BREK=$(jq -n '{a: {b: "q"}}') ts-node src/index.ts

# Override the postgres property wth an environment variable
DATABASE_URL="postgres://user:pass@localhost:5432/db"
BREK=$(jq -n --arg db "$DATABASE_URL" '{postgres: $db}') ts-node src/index.ts

Environment variables in config files

You can use environment variables as values by wrapping it in ${...}. For example, to use environment variable FOO, use ${FOO}. This will translate to process.env.FOO. These will always be typed as strings. Example config file:

{
  "foo": "${FOO}"
}

Loaders

Loaders are custom functions that are called during startup (run-time). This can be used to do anything, such as fetching secrets from AWS Secrets Manager, or any other dynamic runtime operation. They can be Promise/async/await based.

Example

conf/default.json

{
  "foo": {
    "[fetchSecret]": {
      "key": "demo"
    }   
  },
  "bar": {
    "[add10]": 42
  }
}

index.ts

import {loadConfig, getConf} from "brek";

const loaders = {
    fetchSecret: (params: {key: string}) => Promise.resolve(`secret_${a}`),
    add10: (val: number) => String(val + 10),
};

loadConfig(loaders)
    .then(() => {
        console.log(getConf().foo); // "secret_demo"
        console.log(getConf().bar); // "52"
        //start server, etc.
    })
    .catch(console.log.bind(console));

Usage

Loader functions must extend (params: any) => string. If helpful, you can import the Loader type like so:

import type {Loader} from 'brek';

In a conf file, any object with a single property matching the pattern wrapped in square brackets ([...]) is assumed to be a loader. The key is the loader name, and the value is the parameter passed to the loader.

If a matching loader is not found, it will throw a LoaderNotFound error. Loaders must return strings.

Recommended best practices

  • default.json should contain all of your local development settings, and then "progressively enhance" from there.
  • Use AWS Secrets Manager or Hashicorp Vault to store your sensitive information and use a loader to load them.

Debugging

You can set the LAMBDA_CONF_DEBUG environment variable to see debug output. Example:

LAMBDA_CONF_DEBUG=1 ts-node src/index.ts

Use with caution! This may output sensitive information to the console.

Known issues

  1. Some IDEs (particularly IntelliJ/Webstorm) occasionally have some issues with caching of the generated Conf.d.ts file (which is stored in your conf folder). If you run into this problem, restarting your TS service.

Support, feedback, and contributions

  • Star this repo if you like it!
  • Submit an issue with your problem, feature request or bug report
  • Issue a PR against main and request review. Make sure all tests pass and coverage is good.
  • Write about this project in your blog, tweet about it, or share it with your friends!

Sponsorship

Aeroview is a lightning-fast, developer-friendly, and AI-powered logging IDE. Get started for free at https://aeroview.io.

Want to sponsor this project? Reach out.

Other useful libraries

  • autorel: Automate semantic releases based on conventional commits. Similar to semantic-release but much simpler.
  • hoare: An easy-to-use, fast, and defensive JS/TS test runner designed to help you to write simple, readable, and maintainable tests.
  • jsout: A Syslog-compatible, small, and simple logger for Typescript/Javascript projects.
  • cjs-mock: NodeJS module mocking for CJS (CommonJS) modules for unit testing purposes.
  • typura: Simple and extensible runtime input validation for TS/JS, written in TS.

License

MIT