You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Express项目配置文件提前加载致Env变量未定义的无重复dotenv解决方法

问题:Socket配置文件提前加载导致环境变量未定义

在Express项目中,server.ts是入口文件,app.ts中调用dotenv.config()加载环境变量,但config/socket-config.ts的顶级代码在app.ts之前执行,导致process.env.WHITELIST_ORIGINS为undefined,希望找到无需在socket-config中重复调用dotenv的解决办法。

相关代码

server.ts

import http from 'http'
import { Socket } from 'socket.io'
import app from './app'
import { setIO } from './config'

const server = http.createServer(app)

const io = setIO(server)

io.on('connection', function (socket: Socket) {
  console.log('New client connected with id => ', socket.id)

  socket.on('disconnect', function (reason): void {
    console.log('A client disconnected with id => ', socket.id, ' reason => ', reason)
  })
})

const port = process.env.PORT || 8095

server.listen(port, () => {
  console.log(`App running`)

})

console.log('server call')

app.ts

import compression from 'compression'
import cors from 'cors'
import dotenv from 'dotenv'
import express, { Application } from 'express'
import helmet from 'helmet'
import { errorHandler, verifyToken } from './middleware'
import router from './router'

dotenv.config()

const app: Application = express()

app.use(compression())
app.use(cors())
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.use(helmet())
app.use(verifyToken)
app.use('/', router)
app.use(errorHandler)

console.log('app call')

export default app

config/socket-config.ts

import { Server as HttpServer } from 'http'
import { Server } from 'socket.io'
// import dotenv from 'dotenv'
// dotenv.config() - 如果在这里调用就能正常工作

let _io: Server

const whitelistOrigins = process.env.WHITELIST_ORIGINS
console.log("whitelistOrigins", whitelistOrigins) // 这里输出undefined

console.log('socket call')

export const setIO = (server: HttpServer): Server => {
  _io = new Server(server, {
    cors: {
      origin: whitelistOrigins,
      methods: ['GET', 'POST'],
      credentials: true,
    },
    allowEIO3: true,
  })
  return _io
}

export const getIO = () => {
  return _io
}

控制台日志顺序

whitelistOrigins undefined
socket call
app call
server call
App running

原因分析

ES模块采用静态解析加载机制:当server.ts导入./app和./config时,Node.js会优先加载所有被导入的模块,执行它们的顶级代码(即函数外部的代码)。socket-config.ts里的const whitelistOrigins = process.env.WHITELIST_ORIGINS和控制台打印代码,会在app.ts中的dotenv.config()执行前就运行,此时环境变量还未被初始化,所以变量值为undefined。

解决方案

方案1:将dotenv调用移到入口文件最顶部

修改server.ts,在所有模块导入前先初始化dotenv,确保所有模块加载时环境变量已就绪:

// server.ts 最顶部
import dotenv from 'dotenv'
dotenv.config()

// 后续导入其他模块
import http from 'http'
import { Socket } from 'socket.io'
import app from './app'
import { setIO } from './config'

// ... 剩余代码保持不变

方案2:延迟环境变量的获取到函数内部

修改socket-config.ts,把环境变量的获取逻辑移到setIO函数内部,利用函数调用时机确保环境变量已加载:

import { Server as HttpServer } from 'http'
import { Server } from 'socket.io'

let _io: Server

export const setIO = (server: HttpServer): Server => {
  // 在这里获取环境变量,此时app.ts已执行dotenv.config()
  const whitelistOrigins = process.env.WHITELIST_ORIGINS
  console.log("whitelistOrigins", whitelistOrigins)
  
  _io = new Server(server, {
    cors: {
      origin: whitelistOrigins,
      methods: ['GET', 'POST'],
      credentials: true,
    },
    allowEIO3: true,
  })
  return _io
}

export const getIO = () => {
  return _io
}

console.log('socket call')

内容的提问来源于stack exchange,提问作者paperskyline

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 21:18:19