配置管理 #
一、配置概述 #
良好的配置管理是应用开发的基础,Fastify支持多种配置方式。
1.1 配置类型 #
| 配置类型 | 说明 | 适用场景 |
|---|---|---|
| 环境变量 | 系统级配置 | 敏感信息、环境区分 |
| 配置文件 | 项目级配置 | 复杂配置、默认值 |
| 命令行参数 | 启动时配置 | 临时覆盖 |
| 代码配置 | 硬编码配置 | 固定配置 |
1.2 配置优先级 #
text
命令行参数 > 环境变量 > 配置文件 > 代码默认值
二、环境变量 #
2.1 使用dotenv #
安装:
bash
npm install dotenv
使用:
javascript
require('dotenv').config()
const fastify = require('fastify')({
logger: {
level: process.env.LOG_LEVEL || 'info'
}
})
fastify.listen({
port: process.env.PORT || 3000,
host: process.env.HOST || '0.0.0.0'
})
2.2 .env文件 #
.env:
env
NODE_ENV=development
PORT=3000
HOST=0.0.0.0
LOG_LEVEL=debug
DATABASE_URL=mongodb://localhost:27017/myapp
DATABASE_NAME=myapp_dev
REDIS_URL=redis://localhost:6379
JWT_SECRET=your-secret-key
JWT_EXPIRES_IN=7d
API_KEY=your-api-key
.env.example:
env
NODE_ENV=development
PORT=3000
HOST=0.0.0.0
LOG_LEVEL=info
DATABASE_URL=
DATABASE_NAME=
REDIS_URL=
JWT_SECRET=
JWT_EXPIRES_IN=7d
API_KEY=
2.3 环境区分 #
javascript
require('dotenv').config({
path: `.env.${process.env.NODE_ENV || 'development'}`
})
.env.development:
env
NODE_ENV=development
LOG_LEVEL=debug
DATABASE_URL=mongodb://localhost:27017/myapp_dev
.env.production:
env
NODE_ENV=production
LOG_LEVEL=info
DATABASE_URL=mongodb://production-server:27017/myapp
.env.test:
env
NODE_ENV=test
LOG_LEVEL=error
DATABASE_URL=mongodb://localhost:27017/myapp_test
2.4 类型转换 #
javascript
const config = {
port: parseInt(process.env.PORT, 10) || 3000,
host: process.env.HOST || '0.0.0.0',
debug: process.env.DEBUG === 'true',
timeout: parseInt(process.env.TIMEOUT, 10) || 5000,
features: (process.env.FEATURES || '').split(',').filter(Boolean)
}
三、配置文件 #
3.1 JSON配置文件 #
config/default.json:
json
{
"app": {
"name": "My Fastify App",
"version": "1.0.0"
},
"server": {
"port": 3000,
"host": "0.0.0.0"
},
"database": {
"url": "mongodb://localhost:27017",
"name": "myapp"
},
"redis": {
"url": "redis://localhost:6379"
},
"jwt": {
"secret": "default-secret",
"expiresIn": "7d"
}
}
config/development.json:
json
{
"server": {
"port": 3000
},
"database": {
"name": "myapp_dev"
}
}
config/production.json:
json
{
"server": {
"port": 80
},
"database": {
"name": "myapp"
}
}
加载配置:
javascript
const fs = require('fs')
const path = require('path')
function loadConfig() {
const env = process.env.NODE_ENV || 'development'
const defaultConfig = JSON.parse(
fs.readFileSync(path.join(__dirname, 'config/default.json'), 'utf8')
)
const envConfig = JSON.parse(
fs.readFileSync(path.join(__dirname, `config/${env}.json`), 'utf8')
)
return deepMerge(defaultConfig, envConfig)
}
function deepMerge(target, source) {
const result = { ...target }
for (const key in source) {
if (source[key] instanceof Object && key in target) {
result[key] = deepMerge(target[key], source[key])
} else {
result[key] = source[key]
}
}
return result
}
const config = loadConfig()
3.2 JavaScript配置文件 #
config/index.js:
javascript
const env = process.env.NODE_ENV || 'development'
const config = {
development: {
app: {
name: 'My App (Dev)'
},
server: {
port: 3000,
host: 'localhost'
},
database: {
url: 'mongodb://localhost:27017',
name: 'myapp_dev'
},
log: {
level: 'debug',
prettyPrint: true
}
},
production: {
app: {
name: 'My App'
},
server: {
port: process.env.PORT || 80,
host: '0.0.0.0'
},
database: {
url: process.env.DATABASE_URL,
name: 'myapp'
},
log: {
level: 'info',
prettyPrint: false
}
},
test: {
app: {
name: 'My App (Test)'
},
server: {
port: 0,
host: 'localhost'
},
database: {
url: 'mongodb://localhost:27017',
name: 'myapp_test'
},
log: {
level: 'error',
prettyPrint: false
}
}
}
module.exports = config[env]
3.3 使用配置 #
javascript
const config = require('./config')
const fastify = require('fastify')({
logger: {
level: config.log.level,
transport: config.log.prettyPrint
? { target: 'pino-pretty' }
: undefined
}
})
fastify.register(require('@fastify/mongodb'), {
url: `${config.database.url}/${config.database.name}`
})
fastify.listen({
port: config.server.port,
host: config.server.host
})
四、配置插件 #
4.1 配置插件封装 #
javascript
const fp = require('fastify-plugin')
async function configPlugin(fastify, opts) {
const env = process.env.NODE_ENV || 'development'
const config = {
env,
isDev: env === 'development',
isProd: env === 'production',
isTest: env === 'test',
app: {
name: process.env.APP_NAME || 'My App',
version: process.env.APP_VERSION || '1.0.0'
},
server: {
port: parseInt(process.env.PORT, 10) || 3000,
host: process.env.HOST || '0.0.0.0'
},
database: {
url: process.env.DATABASE_URL || 'mongodb://localhost:27017',
name: process.env.DATABASE_NAME || 'myapp'
},
jwt: {
secret: process.env.JWT_SECRET || 'secret',
expiresIn: process.env.JWT_EXPIRES_IN || '7d'
}
}
fastify.decorate('config', config)
}
module.exports = fp(configPlugin, {
name: 'config'
})
4.2 使用配置插件 #
javascript
const fastify = require('fastify')()
fastify.register(require('./plugins/config'))
fastify.register(require('@fastify/mongodb'), {
url: `${fastify.config.database.url}/${fastify.config.database.name}`
})
fastify.listen({
port: fastify.config.server.port,
host: fastify.config.server.host
})
五、配置验证 #
5.1 必需配置检查 #
javascript
function validateConfig(config) {
const required = [
'DATABASE_URL',
'JWT_SECRET'
]
const missing = required.filter(key => !process.env[key])
if (missing.length > 0) {
throw new Error(`Missing required environment variables: ${missing.join(', ')}`)
}
}
validateConfig(process.env)
5.2 配置Schema验证 #
javascript
const Ajv = require('ajv')
const ajv = new Ajv()
const configSchema = {
type: 'object',
required: ['server', 'database'],
properties: {
server: {
type: 'object',
required: ['port', 'host'],
properties: {
port: { type: 'integer', minimum: 1, maximum: 65535 },
host: { type: 'string' }
}
},
database: {
type: 'object',
required: ['url', 'name'],
properties: {
url: { type: 'string', format: 'uri' },
name: { type: 'string', minLength: 1 }
}
}
}
}
function validateConfig(config) {
const validate = ajv.compile(configSchema)
if (!validate(config)) {
throw new Error(`Invalid config: ${ajv.errorsText(validate.errors)}`)
}
}
validateConfig(config)
六、敏感配置管理 #
6.1 加密配置 #
javascript
const crypto = require('crypto')
function encrypt(text, secret) {
const iv = crypto.randomBytes(16)
const key = crypto.scryptSync(secret, 'salt', 32)
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv)
let encrypted = cipher.update(text, 'utf8', 'hex')
encrypted += cipher.final('hex')
return iv.toString('hex') + ':' + encrypted
}
function decrypt(text, secret) {
const [ivHex, encrypted] = text.split(':')
const iv = Buffer.from(ivHex, 'hex')
const key = crypto.scryptSync(secret, 'salt', 32)
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv)
let decrypted = decipher.update(encrypted, 'hex', 'utf8')
decrypted += decipher.final('utf8')
return decrypted
}
6.2 使用密钥管理服务 #
javascript
async function loadSecrets() {
if (process.env.NODE_ENV === 'production') {
const secrets = await fetchSecretsFromVault()
return secrets
}
return {
JWT_SECRET: process.env.JWT_SECRET,
DATABASE_PASSWORD: process.env.DATABASE_PASSWORD
}
}
七、配置热更新 #
7.1 监听配置文件变化 #
javascript
const fs = require('fs')
const path = require('path')
let config = JSON.parse(
fs.readFileSync(path.join(__dirname, 'config.json'), 'utf8')
)
fs.watch(path.join(__dirname, 'config.json'), (eventType, filename) => {
if (eventType === 'change') {
try {
config = JSON.parse(
fs.readFileSync(path.join(__dirname, 'config.json'), 'utf8')
)
console.log('Config reloaded')
} catch (err) {
console.error('Failed to reload config:', err)
}
}
})
7.2 配置更新通知 #
javascript
const EventEmitter = require('events')
class ConfigManager extends EventEmitter {
constructor() {
super()
this.config = {}
}
update(newConfig) {
const oldConfig = this.config
this.config = newConfig
this.emit('update', { oldConfig, newConfig })
}
}
const configManager = new ConfigManager()
configManager.on('update', ({ oldConfig, newConfig }) => {
console.log('Config updated')
})
八、最佳实践 #
8.1 配置分层 #
javascript
const config = {
defaults: {
port: 3000,
host: 'localhost'
},
env: {
port: parseInt(process.env.PORT, 10),
host: process.env.HOST
},
file: loadConfigFile(),
get merged() {
return {
...this.defaults,
...this.file,
...Object.fromEntries(
Object.entries(this.env).filter(([_, v]) => v !== undefined)
)
}
}
}
8.2 配置文档 #
javascript
const configSchema = {
PORT: {
type: 'integer',
default: 3000,
description: 'Server port',
env: 'PORT'
},
HOST: {
type: 'string',
default: '0.0.0.0',
description: 'Server host',
env: 'HOST'
},
DATABASE_URL: {
type: 'string',
required: true,
description: 'Database connection URL',
env: 'DATABASE_URL'
}
}
8.3 配置日志 #
javascript
function logConfig(config) {
const safeConfig = { ...config }
if (safeConfig.jwt) {
safeConfig.jwt = { ...safeConfig.jwt, secret: '***' }
}
if (safeConfig.database) {
safeConfig.database = { ...safeConfig.database, password: '***' }
}
console.log('Loaded config:', JSON.stringify(safeConfig, null, 2))
}
九、总结 #
本章我们学习了:
- 配置类型:环境变量、配置文件、命令行参数
- 环境变量:dotenv使用、.env文件、环境区分
- 配置文件:JSON配置、JavaScript配置
- 配置插件:封装配置插件
- 配置验证:必需检查、Schema验证
- 敏感配置:加密、密钥管理
- 配置热更新:监听变化、更新通知
- 最佳实践:分层、文档、日志
接下来让我们学习日志系统!
最后更新:2026-03-28