¿Cómo almacenar las configuraciones / archivos de configuración de implementación de Node.js?

He estado trabajando en algunas aplicaciones de Node y he estado buscando un buen patrón para almacenar la configuración relacionada con la implementación. En el mundo de Django (de donde vengo), la práctica común sería tener un archivo settings.py que contenga la configuración estándar (zona horaria, etc.) y luego un local_settings.py para la configuración específica de implementación, es decir. con qué base de datos hablar, qué socket de Memcache, dirección de correo electrónico para los administradores, etc.

He estado buscando patrones similares para Node. Solo un archivo de configuración sería bueno, así que no tiene que estar atorado con todo lo demás en app.js , pero me parece importante tener una forma de tener la configuración específica del servidor en un archivo que no está en control de fuente. La misma aplicación podría implementarse en diferentes servidores con configuraciones muy diferentes, y tener que lidiar con conflictos de combinación y todo eso no es mi idea de diversión.

Entonces, ¿hay algún tipo de marco / herramienta para esto, o todo el mundo simplemente piratea algo juntos?

Utilizo un package.json para mis paquetes y un config.js para mi configuración, que se ve así:

 var config = {}; config.twitter = {}; config.redis = {}; config.web = {}; config.default_stuff = ['red','green','blue','apple','yellow','orange','politics']; config.twitter.user_name = process.env.TWITTER_USER || 'username'; config.twitter.password= process.env.TWITTER_PASSWORD || 'password'; config.redis.uri = process.env.DUOSTACK_DB_REDIS; config.redis.host = 'hostname'; config.redis.port = 6379; config.web.port = process.env.WEB_PORT || 9980; module.exports = config; 

Cargué la configuración de mi proyecto:

 var config = require('./config'); 

y luego puedo acceder a mis cosas desde config.db_host , config.db_port , etc … Esto me permite usar parámetros codificados, o parámetros almacenados en variables de entorno si no quiero almacenar contraseñas en el control de código fuente.

También genero un package.json e inserto una sección de dependencias:

 "dependencies": { "cradle": "0.5.5", "jade": "0.10.4", "redis": "0.5.11", "socket.io": "0.6.16", "twitter-node": "0.0.2", "express": "2.2.0" } 

Cuando clono el proyecto en mi máquina local, ejecuto npm install para instalar los paquetes. Más información sobre eso aquí .

El proyecto se almacena en GitHub, con controles remotos agregados para mi servidor de producción.

Puede solicitar archivos JSON a partir del Nodo v0.5.x (haciendo referencia a esta respuesta )

config.json:

 { "username" : "root", "password" : "foot" } 

app.js:

 var config = require('./config.json'); log_in(config.username, config.password); 

Mucho más tarde, encontré un módulo Node.js bastante bueno para administrar la configuración: nconf .

Un simple ejemplo:

 var nconf = require('nconf'); // First consider commandline arguments and environment variables, respectively. nconf.argv().env(); // Then load configuration from a designated file. nconf.file({ file: 'config.json' }); // Provide default values for settings not provided above. nconf.defaults({ 'http': { 'port': 1337 } }); // Once this is in place, you can just use nconf.get to get your settings. // So this would configure `myApp` to listen on port 1337 if the port // has not been overridden by any of the three configuration inputs // mentioned above. myApp.listen(nconf.get('http:port')); 

También admite almacenar configuraciones en Redis , escribir archivos de configuración, y tiene una API bastante sólida, y también está respaldado por una de las tiendas más respetadas de Node.js, Nodejitsu , como parte de la iniciativa del marco Flatiron , por lo que debería ser bastante a prueba de futuro.

Echa un vistazo a nconf en Github .

Mi solución es bastante simple:

Cargue la configuración del entorno en ./config/index.js

 var env = process.env.NODE_ENV || 'development' , cfg = require('./config.'+env); module.exports = cfg; 

Defina algunos valores predeterminados en ./config/config.global.js

 var config = module.exports = {}; config.env = 'development'; config.hostname = 'dev.example.com'; //mongo database config.mongo = {}; config.mongo.uri = process.env.MONGO_URI || 'localhost'; config.mongo.db = 'example_dev'; 

Anule los valores predeterminados en ./config/config.test.js

 var config = require('./config.global'); config.env = 'test'; config.hostname = 'test.example'; config.mongo.db = 'example_test'; module.exports = config; 

Utilizándolo en ./models/user.js:

 var mongoose = require('mongoose') , cfg = require('../config') , db = mongoose.createConnection(cfg.mongo.uri, cfg.mongo.db); 

Ejecutando su aplicación en el entorno de prueba:

 NODE_ENV=test node ./app.js 

Esto se explica con mayor detalle aquí: http://www.chovy.com/node-js/managing-config-variables-inside-a-node-js-application/

También puede consultar dotenv que sigue los principios de una aplicación de doce factores .

Solía ​​usar node-config, pero creé dotenv por ese motivo. Fue completamente inspirado por la biblioteca dotenv de ruby.

El uso es bastante fácil:

 var dotenv = require('dotenv'); dotenv.load(); 

Luego, simplemente crea un archivo .env y coloca tu configuración allí de la siguiente manera:

 S3_BUCKET=YOURS3BUCKET SECRET_KEY=YOURSECRETKEYGOESHERE OTHER_SECRET_STUFF=my_cats_middle_name 

Eso es dotenv para nodejs.

Ustedes están usando npm para comenzar sus scripts (env etc)?

Si usa archivos .env , puede incluirlos en su package.json y usar npm para iniciarlos / iniciarlos.

Ejemplo:

 { "name": "server", "version": "0.0.1", "private": true, "scripts": { "start": "node test.js", "start-dev": "source dev.env; node test.js", "start-prod": "source prod.env; node test.js" }, "dependencies": { "mysql": "*" } } 

luego ejecuta los scripts npm:

 $ npm start-dev 

Se describe aquí https://gist.github.com/ericelliott/4152984 Todo el crédito a Eric Elliot

También puede consultar node-config, que carga el archivo de configuración según $ HOST y la variable $ NODE_ENV (un poco como RoR): documentación .

Esto puede ser bastante útil para diferentes configuraciones de implementación ( development , test o production ).

Simplemente haga un simple settings.js con exports :

 exports.my_password = 'value' 

Luego, en tu script, haz una require :

 var settings = require('./settings.js'); 

Toda su configuración ahora estará disponible a través de la variable de settings :

 settings.my_password // 'value' 

Convict es otra opción que agrega un esquema para la validación. Al igual que nconf, admite la carga de configuraciones desde cualquier combinación de variables de entorno, argumentos, archivos y objetos json.

Ejemplo del archivo README:

 var convict = require('convict'); var conf = convict({ env: { doc: "The applicaton environment.", format: ["production", "development", "test"], default: "development", env: "NODE_ENV" }, ip: { doc: "The IP address to bind.", format: "ipaddress", default: "127.0.0.1", env: "IP_ADDRESS", }, port: { doc: "The port to bind.", format: "port", default: 0, env: "PORT" } }); 

Artículo de introducción : Configuraciones de domesticación con nodo-convicto

Puede usar Konfig para archivos de configuración específicos del entorno. Carga los archivos de configuración json o yaml automáticamente, tiene un valor predeterminado y funciones de configuración dinámica.

Un ejemplo del repository de Konfig:

 File: config/app.json ---------------------------- { "default": { "port": 3000, "cache_assets": true, "secret_key": "7EHDWHD9W9UW9FBFB949394BWYFG8WE78F" }, "development": { "cache_assets": false }, "test": { "port": 3001 }, "staging": { "port": #{process.env.PORT}, "secret_key": "3F8RRJR30UHERGUH8UERHGIUERHG3987GH8" }, "production": { "port": #{process.env.PORT}, "secret_key": "3F8RRJR30UHERGUH8UERHGIUERHG3987GH8" } } 

En desarrollo:

 > config.app.port 3000 

En producción, supongamos que comenzamos la aplicación con $ NODE_ENV=production PORT=4567 node app.js

 > config.app.port 4567 

Más detalles: https://github.com/vngrs/konfig

Voy a poner mi sombrero en el ring porque ninguna de estas respuestas aborda todos los componentes críticos que prácticamente cualquier sistema necesita. Consideraciones:

  • Configuración pública (que se puede ver por la interfaz) vs configuración privada (chico mograbi consiguió este derecho). Y asegurar que estos se mantengan separados.
  • Secretos como llaves
  • Predeterminados versus anulaciones específicas del entorno
  • Paquetes frontend

Así es como hago mi configuración:

  • config.default.private.js : en el control de versiones, estas son opciones de configuración predeterminadas que solo puede ver tu back-end.
  • config.default.public.js – En el control de versiones, estas son las opciones de configuración predeterminadas que se pueden ver por backend y frontend
  • config.dev.private.js – Si necesita diferentes valores predeterminados privados para dev.
  • config.dev.public.js – Si necesita diferentes valores predeterminados públicos para dev.
  • config.private.js – No en control de versión, estas son opciones específicas del entorno que anulan config.default.private.js
  • config.public.js – No en control de versión, estas son opciones específicas del entorno que anulan config.default.public.js
  • keys/ – Una carpeta donde cada archivo almacena un secreto diferente de algún tipo. Esto tampoco está bajo control de versión (las claves nunca deben estar bajo control de versión).

Utilizo archivos Javascript simples para la configuración, así que tengo todo el poder del lenguaje javascript (incluidos los comentarios y la capacidad de hacer cosas como cargar el archivo de configuración predeterminado en el archivo específico del entorno para que puedan ser reemplazados). Si desea usar variables de entorno, puede cargarlas dentro de esos archivos de configuración (aunque recomiendo usar env vars por la misma razón que no recomiendo usar archivos json; no tiene la potencia de un lenguaje de progtwigción para construir tu configuración).

La razón por la cual cada clave está en un archivo separado es para uso del instalador. Esto le permite tener un instalador que crea claves en la máquina y las almacena en la carpeta de claves. Sin esto, su instalador podría fallar cuando cargue su archivo de configuración que no puede acceder a sus claves. De esta forma, puede recorrer el directorio y cargar cualquier archivo clave que esté en esa carpeta sin tener que preocuparse por lo que existe y lo que no en una versión dada de su código.

Dado que probablemente tenga las teclas cargadas en su configuración privada, definitivamente no desea cargar su configuración privada en ningún código de interfaz. Si bien es probable que sea estrictamente más ideal para separar por completo la base de código de su frontend de su servidor, muchas veces PITA es una barrera lo suficientemente grande para evitar que las personas lo hagan, por lo tanto, las configuraciones privadas frente a las públicas. Pero hay dos cosas que hago para evitar que se cargue la configuración privada en la interfaz:

  1. Tengo una prueba de unidad que asegura que mis paquetes de frontend no contengan una de las claves secretas que tengo en la configuración privada.
  2. Tengo mi código frontend en una carpeta diferente que mi código backend, y tengo dos archivos diferentes llamados “config.js”, uno para cada extremo. Para backend, config.js carga la configuración privada, para frontend, carga la configuración pública. Entonces siempre solo necesitas (‘config’) y no te preocupes de dónde viene.

Una última cosa: su configuración debe cargarse en el navegador a través de un archivo completamente separado que ninguno de sus otros códigos frontales. Si agrupa el código de su interfaz, la configuración pública debe construirse como un paquete completamente separado. De lo contrario, tu configuración ya no es realmente config, es solo parte de tu código. La configuración necesita poder ser diferente en diferentes máquinas.

config.js una carpeta como config. Un nombre de archivo como config.js y luego config.js este archivo cuando sea necesario como se muestra a continuación.

Ejemplo de config.js

 module.exports = { proxyURL: 'http://url:port', TWITTER: { consumerkey: 'yourconsumerkey', consumerSecrete: 'yourconsumersecrete' }, GOOGLE: { consumerkey: 'yourconsumerkey', consumerSecrete: 'yourconsumersecrete' }, FACEBOOK: { consumerkey: 'yourconsumerkey', consumerSecrete: 'yourconsumersecrete' } } 

Entonces, si quiero usar este archivo de configuración en alguna parte

Primero importaré como abajo

var config = require('./config');

y puedo acceder a los valores como a continuación

 const oauth = OAuth({ consumer: { key: config.TWITTER.consumerkey, secret: config.TWITTER.consumerSecrete }, signature_method: 'HMAC-SHA1', hash_function(base_string, key) { return crypto.createHmac('sha1', key).update(base_string).digest('base64'); } }); 

Estoy un poco tarde en el juego, pero no pude encontrar lo que necesitaba aquí ni en ningún otro lado, así que escribí algo yo mismo.

Mis requisitos para un mecanismo de configuración son los siguientes:

  1. Soporte front-end. ¿Cuál es el punto si el front-end no puede usar la configuración?
  2. Admite settings-overrides.js – que tiene el mismo aspecto pero permite anular la configuración en settings.js . La idea aquí es modificar la configuración fácilmente sin cambiar el código. Lo encuentro útil para saas.

Aunque me preocupan menos los entornos de soporte, explicaré cómo agregarlo fácilmente a mi solución

 var publicConfiguration = { "title" : "Hello World" "demoAuthToken" : undefined, "demoUserId" : undefined, "errorEmail" : null // if null we will not send emails on errors. }; var privateConfiguration = { "port":9040, "adminAuthToken":undefined, "adminUserId":undefined } var meConf = null; try{ meConf = require("../conf/dev/meConf"); }catch( e ) { console.log("meConf does not exist. ignoring.. ")} var publicConfigurationInitialized = false; var privateConfigurationInitialized = false; function getPublicConfiguration(){ if (!publicConfigurationInitialized) { publicConfigurationInitialized = true; if (meConf != null) { for (var i in publicConfiguration) { if (meConf.hasOwnProperty(i)) { publicConfiguration[i] = meConf[i]; } } } } return publicConfiguration; } function getPrivateConfiguration(){ if ( !privateConfigurationInitialized ) { privateConfigurationInitialized = true; var pubConf = getPublicConfiguration(); if ( pubConf != null ){ for ( var j in pubConf ){ privateConfiguration[j] = pubConf[j]; } } if ( meConf != null ){ for ( var i in meConf ){ privateConfiguration[i] = meConf[i]; } } } return privateConfiguration; } exports.sendPublicConfiguration = function( req, res ){ var name = req.param("name") || "conf"; res.send( "window." + name + " = " + JSON.stringify(getPublicConfiguration()) + ";"); }; var prConf = getPrivateConfiguration(); if ( prConf != null ){ for ( var i in prConf ){ if ( prConf[i] === undefined ){ throw new Error("undefined configuration [" + i + "]"); } exports[i] = prConf[i]; } } return exports; 

Explicación

  • undefined significa que esta propiedad es obligatoria
  • null significa que es opcional
  • meConf : actualmente el código está dirigido a un archivo en la app . meConf es los archivos de anulación que está dirigido a conf/dev – que es ignorado por mis vcs.
  • publicConfiguration : será visible desde el front-end y el back-end.
  • privateConfiguration : solo será visible desde el back-end.
  • sendPublicConfiguration – una ruta que expondrá la configuración pública y la asignará a una variable global. Por ejemplo, el siguiente código expondrá la configuración pública como la variable global myConf en el front-end. Por defecto usará el nombre de variable global conf .

    app.get (“/ backend / conf”, require (“conf”). sendPublicConfiguration);

Lógica de anulaciones

  • privateConfiguration se fusiona con publicConfiguration y luego meConf.
  • publicConfiguration comprueba cada clave si tiene una anulación, y utiliza esa anulación. De esta forma no estamos exponiendo nada privado.

Agregar soporte de entorno

Aunque no encuentro útil un “soporte ambiental”, tal vez alguien lo hará.

Para agregar soporte de entorno necesita cambiar la statement de requisito meConf a algo como esto (pseudocódigo)

if (entorno == “producción”) {meConf = require (“../ conf / dev / meConf”). producción; }

if (entorno == “desarrollo”) {meConf = require (“../ conf / dev / meConf”). desarrollo; }

Del mismo modo, puede tener un archivo por entorno

  meConf.development.js meConf.production.js 

e importa el correcto. El rest de la lógica permanece igual.

un ejemplo alternativo que acabo de usar porque quería más flexibilidad que un archivo .json típico pero no quería que se abstraiga en una biblioteca que requeriría una dependencia es algo como esto. Básicamente, exportar una función invocada inmediatamente que devuelve un objeto con los valores que quería establecer. Da mucha flexibilidad.

  module.exports = function(){ switch(node_env){ case 'dev': return { var1 = 'development'}; } }(); 

Hay una explicación mucho mejor con un ejemplo completo aquí. Usar archivos de configuración en Node.js

Sé que esta es una publicación realmente antigua. Pero quiero compartir mi módulo para configurar variables de entorno, creo que es una solución muy flexible. Aquí está el módulo json-configurator

 var configJson = { 'baseUrl': 'http://test.com', '$prod_baseUrl': 'https://prod.com', 'endpoints': { 'users': '<%= baseUrl %>/users', 'accounts': '<%= baseUrl %>/accounts' }, foo: 'bar', foobar: 'foobar', $prod_foo: 'foo in prod', $test_foo: 'foo in test', deep:{ veryDeep: { publicKey: 'abc', secret: 'secret', $prod_secret: 'super secret' } } }; var config = require('json-configurator')(configJson, 'prod'); console.log(config.deep.veryDeep.secret) // super secret console.log(config.endpoints.users) // https://prod.com/users 

Luego puede usar process.env.NODE_ENV para obtener todas las variables para su entorno.

Además del módulo nconf mencionado en esta respuesta , y node-config mencionado en esta respuesta , también hay node-iniparser e IniReader , que parecen ser analizadores de archivos de configuración .ini más simples.

Recientemente lancé un pequeño módulo para cargar cualquier tipo de archivo de configuración. Es bastante directo, puedes consultarlo en https://github.com/flesler/config-node

Puede usar pconf: https://www.npmjs.com/package/pconf

Ejemplo:

 var Config = require("pconf"); var testConfig = new Config("testConfig"); testConfig.onload = function(){ testConfig.setValue("test", 1); testConfig.getValue("test"); //testConfig.saveConfig(); Not needed } 

Para aquellos que están visitando este hilo antiguo, aquí hay un paquete que considero bueno.

https://www.npmjs.org/package/config

Intenté algunas de las soluciones sugeridas aquí, pero no me sentí satisfecho con ellas, así que creé mi propio módulo. Se llama mikro-config y la principal diferencia es que respeta la convención sobre la configuración, por lo que puede simplemente requerir el módulo y comenzar a usarlo.

Almacena su configuración en archivos js simples o json de la carpeta /config . Primero carga el archivo default.js , luego todos los demás archivos del directorio /config , luego carga la configuración específica del entorno basada en la variable $NODE_ENV .

También permite anular esta configuración para el desarrollo local con local.js o entorno específico /config/env/$NODE_ENV.local.js .

Puede verlo aquí:

https://www.npmjs.com/package/mikro-config

https://github.com/B4nan/mikro-config

Por mucho tiempo, solía usar el enfoque mencionado en la solución aquí. Sin embargo, hay una preocupación sobre la seguridad de los secretos en texto claro. Puede usar otro paquete encima de la config para que se ocupen de los bits de seguridad.

Mira esto: https://www.attosol.com/secure-application-secrets-using-masterkey-in-azure-key-vault/

Es mejor separar las configuraciones de ‘desarrollo’ y ‘producción’ .

Uso de la siguiente manera: Aquí está mi archivo config / index.js :

 const config = { dev : { ip_address : '0.0.0.0', port : 8080, mongo :{ url : "mongodb://localhost:27017/story_box_dev", options : "" } }, prod : { ip_address : '0.0.0.0', port : 3000, mongo :{ url : "mongodb://localhost:27017/story_box_prod", options : "" } } } 

Para requerir el uso de la configuración siguiente:

 const config = require('../config')[process.env.NODE_ENV]; 

Entonces puedes usar tu objeto de configuración:

 const ip_address = config.ip_address; const port = config.port; 

Solo use la npm módulo npm (más de 300000 descargas)

https://www.npmjs.com/package/config

Node-config organiza configuraciones jerárquicas para las implementaciones de su aplicación.

Le permite definir un conjunto de parámetros predeterminados y ampliarlos para diferentes entornos de despliegue (desarrollo, qa, assembly, producción, etc.).

 $ npm install config $ mkdir config $ vi config/default.json { // Customer module configs "Customer": { "dbConfig": { "host": "localhost", "port": 5984, "dbName": "customers" }, "credit": { "initialLimit": 100, // Set low for development "initialDays": 1 } } } $ vi config/production.json { "Customer": { "dbConfig": { "host": "prod-db-server" }, "credit": { "initialDays": 30 } } } $ vi index.js var config = require('config'); //... var dbConfig = config.get('Customer.dbConfig'); db.connect(dbConfig, ...); if (config.has('optionalFeature.detail')) { var detail = config.get('optionalFeature.detail'); //... } $ export NODE_ENV=production $ node index.js