in my app the server.ts
is the entrypoint which serves the app in port. all app.use is done in the app.ts
file where the dotenv
is called. As far as I know you are only supposed to call dotenv
once and the entire app should have the variables. But for some reason the config file gets loaded first before the app or server files which is when the env variable process.env.WHITELIST_ORIGINS
starts with undefined. I cannot understand the reason why this is happening. Is there any way to fix this without having to call dotenv
again from the config file
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() - If this is called here it works fine.
let _io: Server
const whitelistOrigins = process.env.WHITELIST_ORIGINS
console.log("whitelistOrigins", whitelistOrigins) // This logs undefined
console.log('whitelistOrigins', whitelistOrigins)
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
}
console.log('socket call')
Here is the order of how console logs
whitelistOrigins undefined
socket call
app call
server call
App running
The issue is described in detail within the dotenv docs
When you run a module containing an
import
declaration, the modules it imports are loaded first, then each module body is executed in a depth-first traversal of the dependency graph, avoiding cycles by skipping anything already executed.
For this reason, dotenv provides a specific import that executes config()
within its body.
import 'dotenv/config';
It's best to add this as close to your app's entry-point as possible. I would place this first in server.ts