在应用程序中分离 "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.json
和 production.json
,因此您的密码对于不需要知道它是什么的开发人员来说是安全的。如果开发人员需要配置文件才能在应用程序上运行,只需与他们共享 development.json
的加密版本即可。这并不理想,但至少你的密码没有以明文形式存储在 GitHub. 中
- 如果需要添加其他环境(如
test
或stage
),只需要在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.abc123
是 undefined
,但这不一定是坏事(有意想不到的环境不是一个好主意),您还可以改进我提供的代码来处理这种情况。
今天我试图找到一种方法来分隔模式之间的全局变量: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.json
和production.json
,因此您的密码对于不需要知道它是什么的开发人员来说是安全的。如果开发人员需要配置文件才能在应用程序上运行,只需与他们共享development.json
的加密版本即可。这并不理想,但至少你的密码没有以明文形式存储在 GitHub. 中
- 如果需要添加其他环境(如
test
或stage
),只需要在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.abc123
是undefined
,但这不一定是坏事(有意想不到的环境不是一个好主意),您还可以改进我提供的代码来处理这种情况。