在应用程序中分离“开发”和“生产”模式的最佳方法是什么?

时间:2019-12-19 18:52:07

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

今天,我试图找到一种方法来分隔模式(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);
  })


数据库类


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

1 个答案:

答案 0 :(得分:0)

我认为您在正确的道路上。在我看来,抽象出特定于环境的配置是一种方法。

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

  • 我认为您不一定需要dotenv中的merge甚至是lodash来确保您的应用程序代码在不受环境限制的情况下都可以运行。
  • 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文件,并在{{1 }}(例如package.json"start:stage": "NODE_ENV=stage node server")。无需在"test": "NODE_ENV=test mocha"中添加if语句。

一个小缺点:

  • 如果environment/index.js的值是非预期环境(例如NODE_ENV)的字符串,则应用将崩溃,因为NODE_ENV=abc123configuration.abc123,但是这不一定是一件坏事(拥有意外的环境不是一个好主意),并且您还可以改善我提供的用于处理这种情况的代码。