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

config-srv

v3.0.32

Published

API and REST interface for editing a structured set of parameters

Downloads

159

Readme

NPM Version   Build Status  

Edit the configuration parameters through the API / REST API

Service provides the ability to edit hierarchically organized parameter values for your Node.js application through the API or through the REST API.

The structure of the parameters is specified in the Schema file, which can be used to build the parameter editing interface.

Project Guidelines

  • Simple - Get started fast
  • Lightweight - No dependencies
  • Predictable - Well tested foundation for module and app developers

Features

  • Editable via API and REST API set of typed parameters
  • Unlimited tree structure of parameters
  • The Schema is conveniently used to automatically build the parameter editing interface
  • Internationalization of Scheme headers
  • Function for obtaining a translation file template
  • Parameter update event notification
  • Ability to add custom parameter types

Quick Start

Install in your app directory, and create default directory structure:

$ npm install config-srv
$ mkdir config
$ cd config
$ mkdir service
$ cd service
$ mkdir config-data

Create Schema file in the folder <app_root>/config/service/:

$ vi schema.js
module.exports = [
    {
        id: 'namedConfig1',
        title: 'Title of namedConfig1',
        t: 'cs:namedConfig1.title',
        type: 'section',
        value: [
            {
                id: 'div11',
                type: 'section',
                title: 'Title of div11',
                t: 'cs:namedConfig1.div11.title',
                value: [
                    {
                        id: 'div111',
                        type: 'string',
                        title: 'Title of div111',
                        t: 'cs:namedConfig1.div11.div111',
                        arbitraryParameter: 'arbitrary parameter value',
                        value: 'default string'
                    }
                ]
            }
        ]
    },
    {
        id: 'namedConfig2',
        title: 'Title of namedConfig2',
        t: 'cs:namedConfig2.title',
        type: 'section',
        value: [
            {
                id: 'div21',
                type: 'array',
                title: 'div21 title',
                t: 'cs:namedConfig2.div21.title',
                value: [1, 2, 3, 4]
            }
        ]
    },
];

Create named config files in the folder <app_root>/config/service/config-data:

$ cd config-data
$ vi namedConfig1.json
{
    "div11": {
        "div111": "actual string"
    }
}
$ vi namedConfig2.json
{
    "div21": [
        5,
        6,
        7
    ]
}

Use it in your code:

$ cd ../../../
$ vi example-api.js
const configService = require('config-srv')();

// Get the Schema and use it
// to build the client-side parameter editing interface
const schemaOfConfig1 = configService.getSchema('namedConfig1');

// Anywhere in the code,
// get the parameter value by its path:
let value = configService.get('namedConfig1.div11.div111');
console.log(value);
// --> actual string

value = configService.getEx('namedConfig1.div11.div111');
console.log(value);
/*
-->
{
    "value": "actual string",
    "defaultValue": "default string",
    "paramPath": "namedConfig1.div11.div111",
    "paramName": "div111"
}
*/
// Set new parameter value:
configService.set('namedConfig1.div11.div111', 'new value 2');

value = configService.get('namedConfig1.div11.div111');
console.log(value);
// -> new value 2

API / REST API methods

<ep> - shorthand for "entry point", for example, http://localhost:8683/config-service

Using REST API

Integrate the service as middleware in express:

$ npm install express
$ cd ../../../
$ vi example-rest.js
const express = require('express');
const app = require('express')();
const webServer = require('http').Server(app);

app.use(express.json()); // to support JSON-encoded bodies

const { rest } = require('config-srv')();

// Use config-service as middleware
app.use(rest);

app.use((req, res) => {
    // All requests that were not processed by the configuration service will be sent here.
    res.status(501).send('Not Implemented');
});

const port = '8683';
const host = 'localhost';

webServer.listen(port, host, () => {
    console.log(`Web-Server listening on http://${host}:${port}`);
});
$ node example-rest.js

Get full Schema:

###
GET http://localhost:8683/config-service?get-schema

Get Schema for the given path:

###
GET http://localhost:8683/config-service?get-schema=namedConfig1.div11

response:

{
    "id": "div11",
    "type": "section",
    "title": "Title of div11",
    "t": "cs:namedConfig1.div11.title",
    "value": [
        {
            "id": "div111",
            "type": "string",
            "title": "Title of div111",
            "t": "cs:namedConfig1.div11.div111",
            "defaultValue": "default string",
            "arbitraryParameter": "arbitrary parameter value",
            "value": "new value 2",
            "path": "namedConfig1.div11.div111"
        }
    ],
    "path": "namedConfig1.div11"
}

Get a list of named configurations:

###
GET http://localhost:8683/config-service?list

response:

[
    "namedConfig1",
    "namedConfig2"
]

Get whole parameter structure:

###
GET http://localhost:8683/config-service?get

response:

{
    "value": {
        "namedConfig1": {
            "div11": {
                "div111": "new value 2"
            }
        },
        "namedConfig2": {
            "div21": [
                5,
                6,
                7
            ]
        }
    }
}

Get parameter value(s) from a given path:

###
GET http://localhost:8683/config-service?get=namedConfig1.div11.div111

response:

{
    "value": "new value 2"
}

Get parameter value(s) from a given path (accompanied by additional information):

###
GET http://localhost:8683/config-service?get-ex=namedConfig1.div11

response:

{
    "value": {
        "div111": "new value 2"
    },
    "defaultValue": {
        "div111": "default string"
    },
    "paramPath": "namedConfig1.div11",
    "paramName": "div11"
}

Set parameter value:

POST http://localhost:8683/config-service?set=namedConfig1.div13.v_json
Content-Type: application/json

{
    "value": {
        "a": 999,
        "b": "content"
    }
}

Get Help:

GET http://localhost:8683/config-service/help

Sample Queries for WebStorm: example.http

Schema file

The schema.js file contains information about the structure of parameters, their types and field titles for the editing interface.

Each parameter is described by an object with the following properties:

When a schema is requested via API/REST, the path property is automatically added to each schema element. It is the path to the schema element. It can be used when getting or saving the value of the element.

In addition to these properties, you can set any others and they will be transferred when requesting a schema through the API. For example, these can be properties for the interface for managing settings, for differentiating rights, etc.

You can specify an additional list of properties that will be translated into other languages when usingi18next. See the translatedProperties property in the chapter "Options when creating a service" below.

Named configuration files

Separation of a configuration into parts

There is a need to edit individual groups of settings independently. That is, the interface should have different pages for different groups of settings. To do this, the parameters are stored in named configurations.

Named configuration files are files with the json extension and names that exactly match the names of the properties of the first level of the Schema object.

When a parameter value changes within a single named configuration, the corresponding file is re-saved. Files of other named configurations are not affected.

Files location

The folder where the service searches for Schema file is by default <app_root>/config/service/. The default folder from which the named configuration files will be read is <app_root>/config/service/config-data.

<app_root>
.
└── config
    └── service
        ├── config-data
        │   ├── named_config_1.json
        │   ├── named_config_2.json
        │   └── ...
        └── schema.js

You can specify a different location for schema.js through the environment variable.

NODE_CONFIG_SERVICE_SCHEMA_DIR

For example:

NODE_CONFIG_SERVICE_SCHEMA_DIR=./config/my            -> <app_root>/config/my/
NODE_CONFIG_SERVICE_SCHEMA_DIR=/opt/node/config/my    -> /opt/node/config/my

Different location for named configurations can be changed by setting the value of the environment variable:

NODE_CONFIG_SERVICE_DIR

Example 1:

NODE_CONFIG_SERVICE_SCHEMA_DIR=./my-config-service
NODE_CONFIG_SERVICE_DIR=named-configs

In this case, the service will work with the following structure:

.
└── my-config-service
    ├── named-configs
    │   ├── named_config_1.json
    │   ├── named_config_2.json
    │   └── ...
    └── schema.js

# If you specify the relative path in `NODE_CONFIG_SERVICE_DIR`,
# then the directory of named configurations will be located relative
# to the directory where` schema.js` is

Example 2:

NODE_CONFIG_SERVICE_SCHEMA_DIR=./my-config-service
NODE_CONFIG_SERVICE_DIR=<abs_app_root_path>/named-configs

In this case, the service will work with the following structure:

.
└── my-config-service
│   └── schema.js
└── named-configs
    ├── named_config_1.json
    ├── named_config_2.json
    └── ...

Types of Parameters

| Standard Schema types | suitable js types | note | example | | --------------------- | ----------------- | ----------------------- | --------------------------------- | | section | object/array | | | | array | object/array | | [1,2, 'str'] | | string | * | | 'any string' | | text | string | | 'any text' | | date | string | YYYY-MM-DD | '2020-06-05' | | time | string | HH:mm:ss.SSS | '14:03:23.478' | | datetime | string | YYYY-MM-DDTHH:mm:ss.SSS | '2020-06-05T14:03:23.478' | | email | string | | '[email protected]' | | number | number, string | | 123.456, '123.567' | | int | number, string | | 12, '13' | | long | number, string | | 9223372036854775000, '92233' | | float | number, string | | 123.456, '123.567' | | double | number, string | | 1234567890.1234567, '123.4567890' | | money | number, string | | 123.456, '123.567' | | boolean (bool) | boolean | | true | | json | * | | { a: 1, b: [1,2,3]} |

Custom parameter types

You can add your own parameter types to the Schema.

To do this, pass the userTypes property to the service constructor as part of the options object.

An object must contain properties of the same name as user types, each of which contains two properties:

If you fill in the error.reason property with an error message, an error of the ConfigServiceError type will be generated. The parameter value will remain the same.

Example:

const userTypes = {
    corpEmail: {
        jsTypes: ['null', 'string'],
        /**
         * Function of validation and normalization of a new value
         *
         * @param {any} newValue - new value
         * @param {schemaItemType} schemaItem - fragment of the schema containing the new value
         * @param {Object} error - container for transmitting validation error message
         * @return {null|any} - normalized value
         */
        validator: (newValue, schemaItem, error = {}) => {
            newValue = String(newValue).trim();
            const match = /^[A-Z._-][email protected]$/i.exec(newValue);
            if (!match) {
                error.reason = `The email address you provided does not apply to "AnyCorp" corporate addresses.`;
                return null;
            }
            return newValue;
        }
    },
}

const configService = require('config-srv')({ userTypes });
// ...

i18n

In order to get localized field headers in the Schema, when initializing the service, you need to pass the i18next object as part of the options.

Example

File structure for example:

<app_root>
.
└── config
│   └── service
│       ├── config-data
│       │   └── config1.json
│       └── schema.js
└── i18n
│   ├── en
│   │   ├── cs.json
│   │   └── translation.json
│   ├── ru
│   │   ├── cs.json
│   │   └── translation.json
│   └── index.js
└── app.js
$ npm install express
$ npm install i18next i18next-sync-fs-backend

File <app_root>/i18n/index.js

const backendI18next = require('i18next-sync-fs-backend');

const preload = ['en', 'ru'];

module.exports = () => {
    const i18next = require('i18next');
    i18next
        .use(backendI18next)
        .init({
            load: 'all',
            initImmediate: false,
            saveMissing: true,
            lng: 'en',
            preload,
            fallbackLng: 'en',
            lowerCaseLng: true,
            ns: ['translation', 'cs'],
            defaultNS: 'translation',
            backend: {
                loadPath: `${__dirname}/{lng}/{ns}.json`,
                addPath: `${__dirname}/{ns}.missing.json`,
                jsonIndent: 4
            },
            saveMissingTo: 'all',
            interpolation: {
                prefix: '{',
                suffix: '}'
            }
        }, () => null);

    return i18next;
};

File <app_root>/i18n/en/cs.json

{
    "__root__title": "TITLE EN for root",
    "config1": {
        "title": "TITLE EN for config1",
        "descr": "DESCRIPTION EN for config1",
        "email": {
            "title": "TITLE EN for config1.email"
        }
    }
}

File <app_root>/i18n/ru/cs.json

{
    "__root__title": "ЗАГОЛОВОК для root",
    "config1": {
        "title": "ЗАГОЛОВОК для config1",
        "descr": "ОПИСАНИЕ for config1",
        "email": {
            "title": "ЗАГОЛОВОК для config1.email"
        }
    }
}

File <app_root>/config/service/schema.js

module.exports = [
    {
        id: 'config1',
        title: 'default title',
        t: 'cs:config1.title',
        type: 'section',
        description: 'cs:config1.descr',
        value: [
            {
                id: 'email',
                type: 'email',
                title: 'default title',
                t: 'cs:config1.email.title',
                value: '[email protected]'
            }
        ]
    }
];

File <app_root>/app.js

const serviceOptions = {
    i18n: require('./i18n/index.js')(),
    i18nNS: 'cs',
    translatedProperties: ['description'],
    writeMissingTranslate: true
};

const configService = require('config-srv')(serviceOptions);
const schemaTranslated = configService.getSchema(null, 'ru');

console.log(JSON.stringify(schemaTranslated, undefined, 2));
{
    "id": "__root__",
    "type": "section",
    "title": "ЗАГОЛОВОК для root",
    "t": "cs:__root__title",
    "value": [
        {
            "id": "config1",
            "title": "ЗАГОЛОВОК для config1",
            "t": "cs:config1.title",
            "type": "section",
            "description": "ОПИСАНИЕ for config1",
            "value": [
                {
                    "id": "email",
                    "type": "email",
                    "title": "ЗАГОЛОВОК для config1.email",
                    "t": "cs:config1.email.title",
                    "value": "[email protected]",
                    "path": "config1.email"
                }
            ],
            "path": "config1"
        }
    ],
    "path": ""
}

Get translation file template

File <app_root>/app.js (continuation)

const templateOptions = {
    lng: 'en',
    onlyStandardPaths: true,
    /*
    If `onlyStandardPaths = false` - the paths for translation id specified in
    the scheme will be added to the resulting object,
    even if they differ from the standard ones.
    For example:
    for the `config1.div13.v_json = {t: 'cs: config1.vjson.title'}` property,
    the `cs:config1.vjson.title` property will be created.
    If `onlyStandardPaths = true` is specified, only
    then the standard property `config1.div13.v_json.title` will be created.
    */
    addPaths: false
    /*
    If `addPaths = true` - next to the title property is
    placed the `t` property containing the translation id.
    This is convenient to use by immediately copying and
    substituting the translation identifier in the code.
    */
}
const templateEn = configService.getTranslationTemplate(templateOptions);
console.log(JSON.stringify(templateEn, undefined, 4));
{
    "__root__title": "TITLE EN for root",
    "config1": {
        "title": "TITLE EN for config1",
        "description": "DESCRIPTION EN for config1",
        "email": {
            "title": "TITLE EN for config1.email"
        }
    }
}

Constructor arguments

Parameter update notifications

A parent application using the component can receive parameter change notifications generated through the REST API.

For this purpose, callback functions are passed to the service constructor as part of the options object.