nestjs configuration with dotenv

There are two problems that make the ConfigService less useful.


When no .env file is present in any environment, readFileSync in


will fail:

[Nest] 63403 [ExceptionHandler] path must be a string or Buffer
TypeError: path must be a string or Buffer
    at Object.fs.openSync (fs.js:646:18)
    at Object.fs.readFileSync (fs.js:551:33)
    at new ConfigService (../config/config.service.ts:8:38)

Even if e.g. process.env.API_KEY is available


will not return anything. So the ConfigService forces you to use a prod.env file, which dotenv advocates against:

No. We strongly recommend against having a "main" .env file and an "environment" .env file like .env.test. Your config should vary between deploys, and you should not be sharing values between environments.


You have to import the config module and inject the service in order to use it. When you use env variables like this

imports: [
  MongooseModule.forRoot(process.env.MONGO_URI, { useNewUrlParser: true }),

the config service is useless.

Read more about config in the environment here:

But this is not recommended to use .env in production environnement. So how to deploy that way ?

Actually, it is not recommended to commit your .env files. It's perfectly fine to use them in production :-).

Why not use the standard method with dotenv and process.env.PORT?

It allows decoupling your core code from the code responsible for providing configuration data. Thus:

  • The core code is easier to test: doing some manual changes/mocking of process.env is such - a - pain, whereas mocking a "ConfigService" is pretty easy
  • You can imagine using anything else than environment variables in the future by just replacing a single method (or a few getters) in a dedicated class, instead of replacing all the occurrences of process.env.* in your code // to be fair, this is unlikely to happen, as using env. variables is the most common way to load configuration data, but still.