在应用程序中分离 "dev" 和 "prod" 模式的最佳方式是什么?

What is the best way of separating "dev" and "prod" modes in the app?

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

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

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

提前致谢!

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


db class


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


environment/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 });

development.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
  }
};


production.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

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

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

  • 我认为您不一定需要 lodash 中的 dotenv 甚至 merge 来确保您的应用程序代码 运行s 无论环境如何英寸
  • 您从 environment/index.js 导出的对象在所有环境中都应具有相同的形状,以避免仅在一种环境中发生的错误,而在您提供的片段中并非如此。
  • 我建议您使用 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"
}

environment/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 ,但这不一定是坏事(有意想不到的环境不是一个好主意),您还可以改进我提供的代码来处理这种情况。