Comment stocker les parameters de déploiement / fichiers de configuration Node.js?

J’ai travaillé sur quelques applications Node et j’ai cherché un bon schéma de stockage des parameters liés au déploiement. Dans le monde de Django (d’où je viens), la pratique courante serait d’avoir un fichier settings.py contenant les parameters standard (fuseau horaire, etc.), puis un local_settings.py pour les parameters spécifiques au déploiement, à savoir. à quelle firebase database parler, quelle socket memcache, adresse e-mail pour les administrateurs, etc.

Je cherchais des modèles similaires pour Node. Juste un fichier de configuration serait bien, donc il ne doit pas être coincé avec tout le rest dans app.js , mais je trouve important d’avoir un moyen d’avoir une configuration spécifique au serveur dans un fichier qui n’est pas sous contrôle de code source. La même application pourrait bien être déployée sur différents serveurs avec des parameters très différents, et le fait de devoir gérer des conflits de fusion et tout cela ne sont pas mes idées amusantes.

Y a-t-il une sorte de framework / outil pour cela, ou est-ce que tout le monde pirate quelque chose ensemble?

J’utilise un package.json pour mes paquets et un config.js pour ma configuration, qui ressemble à config.js :

 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; 

Je charge la configuration de mon projet:

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

et puis je peux accéder à mes choses depuis config.db_host , config.db_port , etc … Cela me permet soit d’utiliser des parameters codés en dur, soit des parameters stockés dans des variables d’environnement si je ne souhaite pas stocker de mots de passe dans le contrôle de code source.

Je génère également un package.json et insère une section de dépendances:

 "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" } 

Lorsque je clone le projet sur mon ordinateur local, je lance npm install pour installer les packages. Plus d’infos à ce sujet ici .

Le projet est stocké dans GitHub, avec des télécommandes ajoutées pour mon serveur de production.

Vous pouvez exiger des fichiers JSON à partir du noeud v0.5.x (en référence à cette réponse )

config.json:

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

app.js:

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

Beaucoup plus tard, j’ai trouvé un très bon module Node.js pour gérer la configuration: nconf .

Un exemple simple:

 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')); 

Il prend également en charge le stockage des parameters dans Redis , l’écriture de fichiers de configuration et une API assez solide. Il est également soutenu par l’une des plus respectées boutiques Node.js, Nodejitsu , dans le cadre de l’initiative Flatiron. assez durable.

Découvrez nconf à Github .

Ma solution est assez simple:

Chargez l’environnement config dans ./config/index.js

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

Définir des parameters par défaut dans ./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'; 

Remplacez les valeurs par défaut dans ./config/config.test.js

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

En l’utilisant dans ./models/user.js:

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

Exécuter votre application dans un environnement de test:

 NODE_ENV=test node ./app.js 

Ceci est expliqué plus en détail ici: http://www.chovy.com/node-js/managing-config-variables-inside-a-node-js-application/

Vous pouvez également regarder dotenv qui suit les principes d’une application à douze facteurs .

J’avais l’habitude d’utiliser node-config, mais j’ai créé dotenv pour cette raison. Il a été complètement inspiré par la bibliothèque dotenv de Ruby.

L’utilisation est assez facile:

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

Ensuite, il vous suffit de créer un fichier .env et d’y placer vos parameters comme suit:

 S3_BUCKET=YOURS3BUCKET SECRET_KEY=YOURSECRETKEYGOESHERE OTHER_SECRET_STUFF=my_cats_middle_name 

C’est dotenv pour nodejs.

Êtes-vous en train d’utiliser npm pour démarrer vos scripts (env, etc.)?

Si vous utilisez des fichiers .env , vous pouvez les inclure dans votre package.json et utiliser npm pour les générer / démarrer.

Exemple:

 { "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": "*" } } 

puis lancez les scripts npm:

 $ npm start-dev 

Son décrit ici https://gist.github.com/ericelliott/4152984 Tout le crédit à Eric Elliot

Vous pouvez également consulter node-config qui charge le fichier de configuration en fonction de $ HOST et de la variable $ NODE_ENV (un peu comme RoR): documentation .

Cela peut être très utile pour différents parameters de déploiement ( development , test ou production ).

Faites simplement un settings.js simple avec des exports :

 exports.my_password = 'value' 

Alors, dans votre script, faites une require :

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

Tous vos parameters seront maintenant disponibles via les settings variable:

 settings.my_password // 'value' 

Convict est une autre option qui ajoute un schéma à la validation. Comme nconf, il prend en charge les parameters de chargement à partir de toute combinaison de variables d’environnement, d’arguments, de fichiers et d’objects json.

Exemple du fichier 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" } }); 

Mise en route de l’article: Configuration des configurations avec noeud-condamné

Vous pouvez utiliser Konfig pour les fichiers de configuration spécifiques à l’environnement. Il charge automatiquement les fichiers de configuration json ou yaml, il a une valeur par défaut et des fonctionnalités de configuration dynamic.

Un exemple de Konfig repo:

 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 développement:

 > config.app.port 3000 

En production, supposons que nous $ NODE_ENV=production PORT=4567 node app.js application avec $ NODE_ENV=production PORT=4567 node app.js

 > config.app.port 4567 

Plus de détails: https://github.com/vngrs/konfig

Je vais jeter mon chapeau sur le ring car aucune de ces réponses ne concerne tous les composants critiques dont n’importe quel système a besoin. Considérations:

  • Configuration publique (qui peut être vue par le frontend) vs configuration privée (guy mograbi a bien compris). Et en veillant à ce qu’ils soient séparés.
  • Des secrets comme des clés
  • Défauts vs substitutions spécifiques à l’environnement
  • Paquets Frontend

Voici comment je fais ma configuration:

  • config.default.private.js – Dans le contrôle de version, ce sont des options de configuration par défaut qui ne peuvent être vues que par votre backend.
  • config.default.public.js – Dans le contrôle de version, ce sont des options de configuration par défaut qui peuvent être vues par le backend et le frontend
  • config.dev.private.js – Si vous avez besoin de valeurs par défaut privées différentes pour dev.
  • config.dev.public.js – Si vous avez besoin de config.dev.public.js défaut différents pour dev.
  • config.private.js – Pas dans le contrôle de version, ce sont des options spécifiques à l’environnement qui remplacent config.default.private.js
  • config.public.js – Pas dans le contrôle de version, ce sont des options spécifiques à l’environnement qui remplacent config.default.public.js
  • keys/ – Un dossier où chaque fichier stocke un secret différent. Ce n’est pas non plus sous contrôle de version (les clés ne doivent jamais être sous contrôle de version).

J’utilise de vieux fichiers javascript pour la configuration. J’ai donc toute la puissance du langage JavaScript (y compris les commentaires et la possibilité de faire des choses comme charger le fichier de configuration par défaut dans le fichier spécifique à l’environnement pour pouvoir le remplacer). Si vous souhaitez utiliser des variables d’environnement, vous pouvez les charger dans ces fichiers de configuration (même si je déconseille d’utiliser env vars pour la même raison que je ne recommande pas l’utilisation de fichiers json – vous ne pouvez pas construire un langage de programmation) votre config).

La raison pour laquelle chaque clé se trouve dans un fichier distinct est destinée à l’installateur. Cela vous permet d’avoir un installateur qui crée des clés sur la machine et les stocke dans le dossier des clés. Sans cela, votre installateur peut échouer lorsque vous chargez votre fichier de configuration qui ne peut pas accéder à vos clés. De cette façon, vous pouvez parcourir le répertoire et charger tous les fichiers de clés qui se trouvent dans ce dossier sans avoir à vous soucier de ce qui existe et de ce qui ne figure dans aucune version de votre code.

Comme vous avez probablement des clés chargées dans votre configuration privée, vous ne voulez certainement pas charger votre configuration privée dans un code frontal. Bien que ce soit probablement plus idéal pour séparer complètement votre base de code frontend de votre backend, PITA est souvent un obstacle assez important pour empêcher les gens de le faire, donc une configuration privée vs publique. Mais il y a deux choses que je fais pour éviter que la configuration privée ne soit chargée dans le frontend:

  1. J’ai un test unitaire qui garantit que mes bundles frontaux ne contiennent aucune des clés secrètes que j’ai dans la configuration privée.
  2. J’ai mon code frontal dans un autre dossier que mon code backend et j’ai deux fichiers différents nommés “config.js” – un pour chaque extrémité. Pour backend, config.js charge la configuration privée, pour frontend, il charge la configuration publique. Ensuite, vous avez toujours besoin (“config”) et ne vous inquiétez pas de son origine.

Une dernière chose: votre configuration doit être chargée dans le navigateur via un fichier complètement séparé de tout autre code frontal. Si vous regroupez votre code frontal, la configuration publique doit être conçue comme un ensemble complètement distinct. Sinon, votre configuration n’est plus vraiment config – c’est juste une partie de votre code. La configuration doit pouvoir être différente sur différentes machines.

Je vais créer un dossier comme config un nom de fichier comme config.js et plus tard je vais utiliser ce fichier partout où cela est requirejs comme ci-dessous

Exemple de config.js

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

Alors si je veux utiliser ce fichier de configuration quelque part

Je vais d’abord importer comme ci-dessous

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

et je peux accéder aux valeurs comme ci-dessous

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

Je suis un peu en retard dans le jeu, mais je ne pouvais pas trouver ce dont j’avais besoin ici – ou ailleurs – alors j’ai moi-même écrit quelque chose.

Mes exigences pour un mécanisme de configuration sont les suivantes:

  1. Supporter le front-end. Quel est le point si le frontal ne peut pas utiliser la configuration?
  2. Support settings-overrides.js – qui a le même aspect mais permet de remplacer la configuration sur settings.js . L’idée ici est de modifier facilement la configuration sans changer le code. Je le trouve utile pour les saas.

Même si je me soucie moins des environnements de support – la volonté expliquera comment l’append facilement à ma solution

 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.ssortingngify(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; 

Explication

  • undefined signifie que cette propriété est requirejse
  • null signifie que c’est facultatif
  • meConf – actuellement, le code est la cible d’un fichier sous l’ app . meConf est le remplacement des fichiers qui est destiné à conf/dev – qui est ignoré par mes vcs.
  • publicConfiguration – sera visible du front-end et du back-end.
  • privateConfiguration – sera visible du back-end uniquement.
  • sendPublicConfiguration – une route qui exposera la configuration publique et l’affectera à une variable globale. Par exemple, le code ci-dessous exposera la configuration publique en tant que variable globale myConf dans le front-end. Par défaut, il utilisera le nom de variable global conf .

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

Logique des remplacements

  • privateConfiguration est fusionné avec publicConfiguration, puis meConf.
  • publicConfiguration vérifie chaque clé si elle est remplacée et utilise cette substitution. De cette façon, nous n’exposons rien de privé.

Ajout de la prise en charge de l’environnement

Même si je ne trouve pas un “support de l’environnement” utile, peut-être que quelqu’un le fera.

Pour append la prise en charge de l’environnement, vous devez modifier la déclaration de besoin de meConf (pseudo-code).

if (environment == “production”) {meConf = require (“../ conf / dev / meConf”). }

if (environment == “development”) {meConf = require (“../ conf / dev / meConf”). }

De même, vous pouvez avoir un fichier par environnement

  meConf.development.js meConf.production.js 

et importer le bon. Le rest de la logique rest le même.

Un exemple que je viens d’utiliser parce que je voulais plus de flexibilité qu’un fichier .json typique, mais que je ne voulais pas que cela soit extrait dans une bibliothèque nécessitant une dépendance, c’est comme ça. En gros, exporter une fonction appelée immédiatement qui a renvoyé un object avec les valeurs que je voulais définir. Donne beaucoup de flexibilité.

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

Il y a une bien meilleure explication avec exemple complet ici. Utiliser des fichiers de configuration dans Node.js

Je sais que c’est un vieux message. Mais je souhaite partager mon module pour la configuration des variables d’environnement, je pense que c’est une solution très flexible. Voici le module 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 

Ensuite, vous pouvez utiliser process.env.NODE_ENV pour obtenir toutes les variables de votre environnement.

Outre le module nconf mentionné dans cette réponse et node-config mentionné dans cette réponse , il existe également node-iniparser et IniReader , qui semblent être des parsingurs de fichiers de configuration .ini plus simples.

Je viens de publier un petit module pour charger tout type de fichiers de configuration. C’est assez simple, vous pouvez le vérifier sur https://github.com/flesler/config-node

Vous pouvez utiliser pconf: https://www.npmjs.com/package/pconf

Exemple:

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

Pour ceux qui visitent ce vieux sujet, voici un paquet que je trouve bon.

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

J’ai essayé certaines des solutions suggérées ici, mais je ne les ai pas satisfaites, alors j’ai créé mon propre module. Il s’appelle mikro-config et la principale différence est qu’il mikro-config convention sur la configuration, donc vous pouvez simplement exiger le module et commencer à l’utiliser.

Vous stockez votre configuration dans le dossier js, ou json files from /config . Tout d’abord, il charge le fichier default.js , puis tous les autres fichiers du répertoire /config , puis il charge la configuration spécifique à l’environnement basée sur la variable $NODE_ENV .

Il permet également de remplacer cette configuration pour le développement local avec local.js ou spécifique à l’environnement /config/env/$NODE_ENV.local.js .

Vous pouvez le regarder ici:

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

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

Pendant longtemps, j’utilisais l’approche mentionnée dans la solution ici. Il existe toutefois un problème de sécurité des secrets dans un texte clair. Vous pouvez utiliser un autre package par-dessus config pour que les bits de sécurité soient pris en charge.

Vérifiez ceci: https://www.attosol.com/secure-application-secrets-using-masterkey-in-azuree-key-vault/

Il est préférable de séparer les configurations de «développement» et de «production» .

J’utilise le moyen suivant: Voici mon fichier 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 : "" } } } 

Pour exiger l’utilisation de la configuration suivante:

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

Que vous pouvez utiliser votre object de configuration:

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

Utilisez simplement la config module npm (plus de 300 000 téléchargements)

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

Node-config organise les configurations hiérarchiques pour les déploiements de votre application.

Il vous permet de définir un ensemble de parameters par défaut et de les étendre pour différents environnements de déploiement (développement, Qa, staging, production, 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