Node.js应用集成dotenvx
首先使用dotenvx命令行在目录下创建对应的.env
和.env.keys
文件,命令如下:
$ dotenvx init
$ dotenvx set HELLO "World"
$ dotenvx encrypt
安装dotenvx npm,命令如下:
$ npm add @dotenvx/dotenvx
创建index.js
文件,代码如下:
require('@dotenvx/dotenvx').config()
// or import '@dotenvx/dotenvx/config' if you're using esm
console.log(`Hello ${process.env.HELLO}`)
Run the application:
$ node index.js
即可看到对应的输出结果:Hello World
。
如果要在服务器或者其他环境中运行该应用,请设置DOTENV_PRIVATE_KEY
环境变量,其值来之于.env.keys
文件。
preload 方式
Bun
如果你使用Bun,那么使用以下代码来加载加密后的.env
文件,命令行为:
bun --preload=./preload-dotenvx-bun.js dotenvx-demo.ts
。
import {$} from "bun";
async function load_dotenvx() {
const env_variables = await $`dotenvx decrypt --dump`.json();
for (const [key, value] of Object.entries(env_variables)) {
process.env[key] = value;
}
}
await load_dotenvx();
Node.js
通过node -r
我们可以提前加载并运行相关的代码,这样就可以在应用启动时自动加载dotenvx的配置。
命令行为:node -r ./dotenvx-preload-nodejs.cjs dotenvx-demo.js
const {execSync} = require('node:child_process');
function load_dotenvx() {
try {
const stdout = execSync('dotenvx decrypt --dump');
const env_variables = JSON.parse(stdout);
for (const [key, value] of Object.entries(env_variables)) {
process.env[key] = value;
}
} catch (error) {
console.error(`Error: ${error}`);
}
}
load_dotenvx();
Deno
借助deno run --preload
命令,我们可以在Deno中预加载dotenvx的配置,命令如下:
$ deno run --allow-run --allow-env --preload dotenvx-preload-deno.js demo.ts
样例代码如下:
async function load_dotenvx() {
let cmd = new Deno.Command("dotenvx", {args: ["decrypt", "--stdout", "--format", "json"]});
const {code, stdout, stderr} = await cmd.output();
const env_variables = JSON.parse(new TextDecoder().decode(stdout));
for (const [key, value] of Object.entries(env_variables)) {
process.env[key] = value;
}
}
await load_dotenvx();
对应Node.js应用,我应该选择哪种方式集成Dotenvx?
对于一个典型的Node.js应用,目前下来有三种方式来集成dotenvx:
- npm开发包
- preload方式
dotenvx run -- node index.js
命令行方式
那么你应该选择哪种方式来集成呢? 这里有几点给大家参考一下:
- npm方式:虽然简单,但是有一个问题就是
dotenvx
的npm包比较大,如果你使用esbuild bundle一下,你会发现最终的文件达到405K左右,这个对于一个小型的Node.js应用来说,实在有点大啦,不值当。 - preload方式:如果是serverless场景,就非常合适,主要是本来就有preload.js文件来初始化环境,所以增加dotenvx的preload代码也很简单。
- 命令行方式:如果你的应该是通过shell脚本启动,那么调用一下
eval $( dotenvx decrypt --format shell --stdout )
可以直接转换为环境变量。。
我个人是不建议npm方式,主要是dotenvx涉及到加解密,npm包的大小会比较大,这样无需会增加应用的体积和消耗的资源,这个要慎重。 至于preload和命令行方式,这个就看你项目具体的情况啦。
npm方式
如果你使用npm方式运行相关的Node.js应用,那么你需要在package.json
中添加以下脚本:
"scripts": {
"dev": "eval $(dotenvx decrypt -f .env --stdout --format shell); tsx watch src/index.ts",
"build": "tsc",
"start": "eval $(dotenvx decrypt -f .env --stdout --format shell); node dist/index.js"
},
解释:
"dev": "eval $(dotenvx decrypt -f .env --stdout --format shell); tsx watch src/index.ts"
: Debugger友好,开发模式下使用dotenvx run -- node dist/index.js
: 简洁模式
其实就是就是通过dotenvx
Rust命令行,将.env
文件解密为shell格式的环境变量,然后通过eval
命令来设置环境变量。
这种方式非常简单,不会占用什么资源,而且可以直接在shell脚本中使用。
参考样例仓库: https://github.com/linux-china/dotenvx-hono-demo
Volta和Dotenvx集成
不少同学使用Volta来管理Node.js版本和工具,
Volta管理Node.js多版本和工具,都是通过一个shim工具来实现的,也就是$HOME/.volta/bin/volta-shim
命令,
然后会会创建一个node
的软连接,指向$HOME/.volta/bin/volta-shim
,在执行node
命令时,实际上是执行的volta-shim
命令,
volta-shim会根据当前目录的package.json
文件,来决定使用哪个版本的Node.js,然后再执行对应版本的Node.js解析器。
接下来我们要做的工作就是让volta-shim支持dotenvx特性,这样就可以让Node.js天生支持dotenvx特性了。 Volta是用Rust语言编写的,
Dotenvx的命令行也是用Rust编写的,所以我们只需要在volta-shim中调用dotenvx-rs的API即可,
也就是一行代码的事:dotenvx_rs::dotenv().ok();
,这样就可以加载.env
文件了,然后再执行对应版本的Node.js解析器即可。
让我们看一下具体的操作。 首先安装Volta,
然后下载Volta Dotenvx Edition,
覆盖$HOME/.volta/bin/
下的命令行即可。
注意: 使用Mac的同学可能要执行一下sudo xattr -r -d com.apple.quarantine volta*
解除安全隔离。
如果是Bun或者Deno,你可以创建一个bunw
或者denow
的脚本,由改脚本负责调用dotenvx加载.env
文件,然后再调用具体的Bun或者Deno解析器即可。
然后将该脚本保存在bun
和deno
命令所在目录下即可。 样例脚本如下:
#!/bin/bash
# load .env by dotenvx
eval $( dotenvx decrypt --stdout --format shell )
# Execute bun command with arguments
$HOME/.bun/bin/bun "$@"
#$HOME/.deno/bin/deno "$@"