简体   繁体   中英

When to load .env variables in NodeJS app?

I am coding a simple NodeJS Express REST API, using TypeScript. I have some environment variables that I load with dotenv .

I access my .env variables at two different stages in my code: index.ts , which is my start file, and in a MyControllerClass.ts file. To access these variables, the code is process.env.MY_ENV_VAR . To load them for the application, the code is dotenv.config() .

As my index.ts file seems to be the root of my program (I configure my app in it), I use dotenv.config() to load my .env file for the rest of the program. However, in my MyControllerClass.ts file, in the constructor, if I do console.log(process.env.MY_ENV_VAR) , I get " undefined ". I could workaround this by adding a dotenv.config() in my constructor (it works) but it's nonsense to me to have it here.

How do I use dotenv.config() once and for all in my program, in a readable manner (like in an appropriate .ts file)? and more generally: what is a NodeJS Express loading cycle?

Here is a sample of the file structure of my code

src
├── index.ts
├── Authentication
│   └── authentication.router.ts
│   └── authentication.controller.ts

Here is the code of index.js

/**
 * Required External Modules
 */
 import * as dotenv from "dotenv";
 import express from "express";
 import cors from "cors";
 import helmet from "helmet";
 import { authenticationRouter } from "./authentication/authentication.router"

 dotenv.config();

 /**
 * App Variables
 */
 if(!process.env.PORT) {
    process.exit(1);
}
const PORT: number = parseInt(process.env.PORT as string, 10);
const app = express();

/**
 *  App Configuration
 */
 app.use(helmet());
 app.use(cors());
 app.use(express.json());
 app.use(authenticationRouter);

 app.use("api/authenticate/", authenticationRouter);
/**
 * Server Activation
 */
 app.listen(PORT, () => {
    console.log(`Listening on port ${PORT}`);
  });

Here is the code of authentication.router.ts

import express, { Request, Response } from "express";
import { AuthenticatorController } from "./authentication.controller";
export const authenticationRouter = express.Router();
const authenticatorController = AuthenticatorController.getInstance();

authenticationRouter.post("/api/authenticate", async (req: Request, res: Response) => {   
    try {
        if (await authenticatorController.authenticate(req.body.login, req.body.password)) {
            res.send({"status": "ok"})
        } else
            res.send({"status": "Error"})
    } catch (e) {
        console.debug(e)
        res.send({"status": "500"});
    }
    
});

Here is the code of authentication.controller.ts

import { ClientSecretCredential } from "@azure/identity";
import { SecretClient } from "@azure/keyvault-secrets";
import { Authenticator } from "./api/Authenticator";
import * as dotenv from "dotenv";
dotenv.config();

export class AuthenticatorController implements Authenticator {
    
    private static singleInstance: AuthenticatorController | null = null;
    private azureSecretCredential= new ClientSecretCredential(
        process.env.AZURE_TENANT_ID as string,
        process.env.AZURE_CLIENT_ID as string,
        process.env.AZURE_CLIENT_SECRET as string);
    private azureSecretClient = new SecretClient(
        process.env.KEY_VAULT_URL as string,
        this.azureSecretCredential);

    private constructor () {}

    public static getInstance(): AuthenticatorController {
        if (this.singleInstance === null) {
            this.singleInstance = new AuthenticatorController();
        }
        return this.singleInstance;
    }

    public async authenticate(login: string, password: string): Promise<Boolean> {
        let isAuthenticated = false;

        try {
            const secret = await this.azureSecretClient.getSecret(login)
            if (secret.name === login) {
                if (secret.value === password) {
                    isAuthenticated = true;
                }
            }
        }   catch (e) {
            console.debug(e);
        }
            return isAuthenticated;
    }
}

You only call dotenv.config() once:

As early as possible in your application, require and configure dotenv.

require('dotenv').config()

Therefore index.ts seems to be correct, process.env should then hold your parsed values. Maybe you can use something like this to make sure, data is parsed correctly:

const result = dotenv.config();

if (result.error) {
  throw result.error;
}

console.log(result.parsed);

Edit:

You can try the following. I changed your exports a bit, because there is no need for a singleton within your controller.

authentication.router.ts:

// Imports (no dotenv; no dotenv.config())
// [...]

// Import controller
import { authenticatorController } from "./authentication.controller";

export const authenticationRouter = express.Router();

// Adding routes
// [...]

authentication.controller.ts:

// Imports (no dotenv; no dotenv.config())
// [...]

class AuthenticatorController implements Authenticator {
  // [...]
}

export const authenticatorController = new AuthenticatorController();

index.ts:

// Imports (dotenv)
// [...]

const { error, parsed } = dotenv.config();

if (error) {
  throw error;
}

console.log(parsed);

// [...]

 app.use("api/authenticate/", authenticationRouter);

// [...]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM