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 🙏

© 2025 – Pkg Stats / Ryan Hefner

iq-config

v0.2.7

Published

Easy configuration for your deployments. Elevate your .env files to the next level.

Downloads

319

Readme

iq-config

Next-Level Configuration Management

iq-config is an easy-to-use configuration utility designed to streamline and simplify working with configuration values in Node.js applications. It brings .env files to the next level by providing type safety, runtime visibility, and structured configuration handling.

Why Use iq-config?

Managing configurations in different deployment environments—On-Premise, SaaS (Single Tenant & Multi-Tenant) — can quickly become complex and error-prone.iq-config solves this by offering:

  • Fully Type-Safe: Configuration values are strictly typed and validated.
  • Seamless TypeScript Support: Get full code completion and editor integration.
  • Self-Documenting: Every configuration value includes descriptions and categorization.
  • Transparent & Editable at Runtime: View and modify configurations dynamically.
  • Works in Node.js & Browser: Use it anywhere without hassle.
  • Lightweight & Minimal Dependencies: No unnecessary bloat.
  • (Optional) UI Integration: Easily inspect and edit configurations visually (coming soon!).

📦 Installation

You can install iq-config via npm or yarn:

npm install iq-config
# or
yarn add iq-config

🔧 Getting Started

1️⃣ Declare Configuration Schema

First, create a ConfigBuilder instance and declare your configuration structure:

import { ConfigBuilder } from "iq-config";

const builder = new ConfigBuilder();
const creator = builder.declare({
  port: builder.variable(
    "port",
    "SERVER_PORT",
    "The server port",
    "Server",
    "required",
    "static",
    3000,
  ),
  url: builder.variable(
    "url",
    "SERVER_URL",
    "The server URL",
    "Server",
    "required",
    "static",
    "http://localhost",
  ),
});

// Define type for the configuration
export type Config = MakeConfigType<typeof creator>;

2️⃣ Initialize Configuration

Next, load configuration values from different sources and build the final configuration:

import fs from "fs-extra";
import dotenv from "dotenv";

export let config: Config;

export const initializeConfig = async () => {
  dotenv.config();
  const custom = await fs.readJson("path/to/config.json");
  const user = await fs.readJson("path/to/user-config.json");

  const result = creator.build([process.env, custom, user]);
  if (result.status === "Error") {
    throw new Error(
      `Failed to initialize configuration: ${result.errors.join("; ")}`,
    );
  }

  config = result.config;
};

📌 Features & API Overview

Initializing and Declaring the Config-System

First create a ConfigBuilder instance to declare the configuration values used.

import { ConfigBuilder } from "iq-config";

const builder = new ConfigBuilder();
const creator = builder.declare({
  // list your config here
});

After declaring the configuration, you can define the Type of your configuration using the MakeConfigType helper. This allows you define a global variable to store the configuration, after initializing your system

type Config = MakeConfigType<typeof creator>;

The steps "declaring" and "creating the type" usually are done in a JS Module, the building in an initialization method.

Creating the Configuration

You now can use the creator to create an instance of the configuration. This usually is done in an initialization method, so you can access asynchronous resources:

let config: Config = undefined as any;
// lying, to not have to deal with null checks later in code.

export const initialize = async () => {
  // Load all config sources, e.g.:
  dotenv.config();
  const custom = await fs.readJson("path/to/config.json");
  const user = await fs.readJson("path/to/config.json");

  // Then build the config:
  let result = creator.build([process.env, custom, user]);
  if (result.status === "Error") {
    throw new Error(
      `Failed to initialize the environment module: ${result.errors.join("; ")}`,
    );
  }

  config = result.config;
};

export const env = (): Config => {
  if (!config) {
    throw new Error("Initialize the environment module first");
  }
  return env;
};

Persisting Changes

The build option also accepts a callback to save configuration changes. The callback will be called every time a value was changed, but it will only include the changed values.

If you want to store the changes in a file, you have to merge the new changes with existing ones, ot not loose data.

let result = creator.build(
    [...],
    async (config) => {
        const settings = {
            ...(await fs.readJson("path/to/config.json")),
            ...config,
        };

        await fs.writeJson("path/to/config.json", user, {
            spaces: 2,
        });
    }
);

Declaring Variables

The builder allows to declare strictly typed configuration variables:

builder.variable(
    "url",              // The variable type
    "SERVER_PORT",      // The variable key, used to access and read the value
    "The server port",  // The description, supports Markdown
    "Server",           // Category, allows grouping of variables
    "required",         // Required or optional, a required variable cannot be undefined
    "static",           // Static variables cannot be changed at runtime.
    "http://google.de", // Optional default value, if the variable is required
                        // and no default value was provided, the variable has
                        // to be supplied during the building.
    // Optional: Custom verification method. The method gets the full
    // initialized configuration. If no error was found, return undefined,
    // otherwise return the description of the error.
    (config) => {
        return "ERROR"
    }
),

The system supports the following types:

  • string
  • ip
  • ipv4
  • ipv6
  • url
  • number
  • int
  • port

If you need at type that is missing here, please create a ticket.

Using the system:

Reading

The config allows reading, listing and changing values:

Reading a single value:

config.get("KEY_NAME");

Reading a multiple values:

config.mGet(["KEY1_NAME", "KEY2", ...]);

Changing

Change a value. The return is either { success: true }, or it will return { success: false, error: "string"}, giving a reason for failing. Common reasons are:

  • failing a validator, e.g. giving an invalid IP for an IP variable
  • failing a custom validator
  • setting null or undefined to a required variable
  • changing a readonly variable
await config.set("KEY_NAME", NEW_VALUE);

Changing multiple types at once. mSet will first apply all changes, then perform validations on the changed object and only apply the changes if no validation errors were found. This is required when multiple values are connected. For example: ENABLE_CACHE=TRUE; REDIS_CONNECTION_STRING=xxx. Once the cache is enabled, the connection string is required.

await config.mSet({
  ENABLE_CACHE: true,
  REDIS_CONNECTION_STRING: "xxx",
});

Subscribing

The system allows to subscribe to configuration changes. The subscription is to a set of configuration keys. If one or multiple of those keys are changed, the callback will be called, with the subscribed keys as argument.

config.subscribe(["ENABLE_CACHE", "REDIS_CONNECTION_STRING"], async (cfg) => {
  console.log(cfg.ENABLE_CACHE);
  console.log(cfg.REDIS_CONNECTION_STRING);
});

🛠 Open Points & Future Enhancements

Highest priority, to to bottom.

  • Automate CI/CD pipeline for releases
  • Improve validator syntax, maybe not attach the validator to single keys, and register them after the declaration, so the type of the config object is known already.
  • Optimize subscription handling for better performance
  • Remove ip-address dependency (only used for IP validation). The goal is 0 dependencies

Why Another Configuration Library?

During one of my projects, the requirements changed, forcing us to support Node.js across multiple deployment models—On-Premise, SaaS Single Tenant, and Multi-Tenant.

Managing diverse configuration options became increasingly complex, leading to inefficiencies and wasted time.

To address this, I needed a configuration system that met the following criteria:

  • ✅ Fully Type-Safe – Ensures configuration values are validated and verified.
  • ✅ Seamless TypeScript Support – Provides full code completion and editor integration.
  • ✅ Easy to Use – Simple API for defining, managing, and retrieving configurations.
  • ✅ Self-Describing – Allows listing all values with descriptions.
  • ✅ Transparent – Configuration values remain visible at runtime.
  • ✅ Editable at Runtime – Supports dynamic modifications.
  • ✅ Cross-Platform – Works in both Node.js and the browser.
  • ✅ Lightweight – Minimal dependencies for efficiency.
  • ✅ (Optional) UI Support – Enables visual inspection and editing (planned as a separate package).

After failing to find an existing library that met all these needs, I decided to build my own. If it helped me, maybe it can help others too! Most existing configuration libraries fall short in one or more areas—whether it's lack of type safety, difficulty in managing configurations dynamically, or limited editor support. iq-config was built to solve real-world problems in large-scale deployments, making it a powerful yet lightweight choice for your projects.


Give it a try and simplify your configuration management! 🚀