【问题标题】:What is the best way of separating "dev" and "prod" modes in the app?在应用程序中分离“开发”和“产品”模式的最佳方法是什么?
【发布时间】:2020-04-12 09:17:15
【问题描述】:

今天我试图找到一种方法来分离模式之间的全局变量:prod 和 dev。

我已经使用第三方模块“dotenv”在“process.env”中隐藏了敏感信息,但无论我处于开发模式还是生产模式,在那里拥有适当的信息仍然会很舒服。例如,如果我在本地工作,我正在使用本地或云测试数据库,当我处于 prod 模式时,我希望拥有真正的数据库的适当凭据。因此它会根据当前模式自动切换。

您可以在下面看到我到目前为止的想法。对于结构问题或实践、经验的任何建议或建议,我将不胜感激。

提前感谢您!

server.js


import { environment } from "./environment";
import { apiExplorer } from "./graphql";
import express from "express";
import { ApolloServer } from "apollo-server-express";
import { database } from "./utils/database";
import { logger } from "./utils/logging";
import { verify } from "./utils/jwt";

database.connect();

apiExplorer.getSchema().then((schema) => {

  // Configure express
  const port = environment.port;
  const app = express();

  // Configure apollo
  const apolloServer = new ApolloServer({
    schema,
    context: ({ req, res }) => {
      const context = [];

      // verify jwt token
      context.authUser = verify(req, res);

      return context;
    },

    formatError: (error) => {
      logger.error(error);
      return error;
    },

    debug: true

  });
  apolloServer.applyMiddleware({ app });

  app.listen({ port }, () => {
    logger.info(`????Server ready at http://localhost:${port}${apolloServer.graphqlPath}`);
  });

})
  .catch((err) => {
    logger.error('Failed to load api', err);
  })


数据库类


import mongoose from 'mongoose';
import { environment } from "../environment";
import { logger } from './logging';

class Database {

  constructor() {
    this._database = 'MongoDB';
    this._username = environment.db.username;
    this._password = environment.db.password;
    this._dbName = environment.db.name;
  }

  connect() {
    mongoose.Promise = global.Promise;

    const url = `mongodb+srv://${this._username}:${this._password}@cocoondb-qx9lu.mongodb.net/${this._dbName}?retryWrites=true&w=majority`;

    try {
      mongoose.connect(url, { useNewUrlParser: true, useUnifiedTopology: true });
      mongoose.connection.once('open', () => logger.info(`Connected to ${this._database}`));
      mongoose.connection.on('error', err => logger.error(err));
    } catch (e) {
      logger.error(`Something went wrong trying to connect to the database: ${this._database}`)
    }
  }
}

export const database = new Database();


环境/index.js


import { development } from './develepment';
import { production } from './production';
import { merge } from "lodash"

const mode = process.env.NODE_ENV ? 'production' : 'development';
const values = process.env.NODE_ENV ? production : development;

export const environment = merge(values, { mode });

开发.js


import dotenv from 'dotenv';
dotenv.config();

export const development = {
  port: 8080,
  db: {
    username: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
    name: process.env.DB_NAME
  }
};


生产.js


import dotenv from 'dotenv';
dotenv.config();

export const production = {
  port: process.env.PORT,
  newfromproduction: 'jkdl',
  db: {
    test: 'test'
  }
};



file structure

src
   -environment
      - index.js
      - development.js
      - production.js
   -graphql
   -models
   -utils
   server.js

.babelrc
.env
.gitignore
package.json

【问题讨论】:

    标签: node.js express structure backend apollo-server


    【解决方案1】:

    我认为你走在正确的道路上。在我看来,抽象出特定于环境的配置是要走的路。

    这里有几句话可以增强您的代码:

    • 我认为您不一定需要dotenv 甚至lodash 中的merge 来确保您的应用程序代码在任何环境下都能运行。
    • 您从environment/index.js 导出的对象在所有环境中都应该具有相同的形状,以避免仅在一个环境中发生错误,而您提供的 sn-ps 中并非如此。
    • 我建议您在配置中使用 JSON 而不是 JS,但这只是一种偏好。
    • 我建议使用尽可能少的环境变量。您可以简单地依赖 NODE_ENV(或您想要的任何单个环境变量名称)并确保在运行 npm start 脚本时定义它。

    这是我建议执行的代码(应将 ALL_CAPS 字符串替换为您的应用在该环境中运行所需的实际值):

    development.json

    {
      "port": 8080,
      "db": {
        "username": "DEVELOPMENT_USERNAME",
        "password": "DEVELOPMENT_PASSWORD",
        "name": "DEVELOPMENT_DATABASE_NAME"
      },
      "newfromproduction": ""
    }
    

    production.json

    {
      "port": 8080,
      "db": {
        "username": "PRODUCTION_USERNAME",
        "password": "PRODUCTION_PASSWORD",
        "name": "PRODUCTION_DATABASE_NAME"
      },
      "newfromproduction": "jkdl"
    }
    

    环境/index.js

    import development from './development.json';
    import production from './production.json';
    
    const { NODE_ENV: mode } = process.env;
    
    const configuration = {
      development,
      production
    };
    
    // using a little bit of destructuring magic but that is not necessary
    export default { ...configuration[mode], mode };
    

    package.json

    "scripts": {
      "start": "NODE_ENV=production node server",
      "start:dev": "NODE_ENV=development nodemon server"
    }
    

    您可以保持server.js 中的代码不变。

    这种方法的几个好处:

    • 您不必在存储库中提交development.jsonproduction.json,因此您的密码对于不需要知道密码是什么的开发人员来说是安全的。如果开发人员需要配置文件在应用程序上工作,只需与他们共享development.json 的加密版本。这并不理想,但至少你的密码没有以明文形式存储在 GitHub 中。
    • 如果需要添加其他环境(如teststage),只需在environment文件夹中创建一个JSON文件,在package.json中添加脚本(如"start:stage": "NODE_ENV=stage node server""test": "NODE_ENV=test mocha")。无需在environment/index.js 中添加if 语句。

    一个小缺点:

    • 如果NODE_ENV 的值是非预期环境的字符串(例如NODE_ENV=abc123),则应用程序将崩溃,因为configuration.abc123undefined,但这不一定是坏事(有意外的环境不是一个好主意),您还可以改进我提供的代码来处理这种情况。

    【讨论】:

    • 非常感谢您的 cmets!
    • 我只是认为敏感信息应该存储在系统环境中的某个地方并被gitignored(不包括.env文件)。在浏览大量人们推荐使用 DOTENV 的文章之前和之后,我从未使用过环境。我真的很想在没有小偷派对模块的情况下编写它,只是为了提高我的技能。我使用 LODASH 是因为起初我认为会有很多信息需要合并。是的!我同意这两种环境应该具有相同的结构。我在那个,但一开始想听听其他人的意见。
    • 是的,我在启动应用程序时在脚本中设置了变量
    • 正如我在这种方法的好处中提到的,您不必提交配置。在投反对票之前,请务必阅读并理解答案。如果需要,您可以使用dotenvlodash,但这不是必需的,即使对于复杂的配置文件也是如此。在过去的 2 年里,我一直在使用在 AWS 上的多个环境中运行的应用程序,所以我对这个主题知道一两件事。事实上,使用 AWS Secrets Manager 之类的东西更适合存储敏感信息。如果您找到比这更好的方法,我很想听听。
    • 实际上,当它已经被否决时,我投了赞成票。感谢您的帮助,我总是很想听听别人的意见。我想提高我在编程方面的技能和经验,并且听到在该领域有技能和经验的人对我来说非常重要
    猜你喜欢
    • 1970-01-01
    • 2019-06-10
    • 1970-01-01
    • 1970-01-01
    • 2019-02-01
    • 2011-07-03
    • 2010-09-09
    • 1970-01-01
    相关资源
    最近更新 更多