Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Importing dotenv in ES6 #89

Closed
Sparragus opened this issue Sep 10, 2015 · 59 comments
Closed

Importing dotenv in ES6 #89

Sparragus opened this issue Sep 10, 2015 · 59 comments

Comments

@Sparragus
Copy link

Support on ES6 has been tricky for me. Yesterday I tried preloading dotenv with iojs -r dotenv/config index.js. However, it didn't work and I couldn't understand why. My app did nothing. After running the app, a second or two later it would finish without doing anything.

I ended up with a very simple way to use dotenv. I simply import it like this:

import {} from 'dotenv/config'
import somethingElse from 'somethingElse'
...
[the rest of your code]

This works because of how ES6 modules imports modules. Before doing anything else in a file (say index.js) it does all the imports first. It does a depth first traversal of these imports, executing any code inside it. If we import dotenv first, it will execute config and add any env variables before doing anything else in the code.

From ES6 In Depth: Modules:

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.

So, importing dotenv on the first line of a bootstrap file in an app will set the env vars for anything that might use them.

I suggest we add a brief section about importing dotenv with ES6 modules to the README. But before sending a pull request and wanted to open it up for discussion here.

Any thoughts?

@motdotla
Copy link
Owner

@jcblw you've done some work with ES6 right?

@maxbeatty
Copy link
Contributor

@Sparragus can you provide a little more code so we can try to reproduce? I've added some examples of how to use dotenv in various ways in #90 including ES6 preloading. Are you using babel or something else for your import syntax? I don't believe the latest node v4.0.0 supports that out of the box yet.

I would caution against using dotenv/config in any context but preloading because it'll be doing additional work parsing command line arguments. import dotenv from 'dotenv' should do the trick

@jcblw
Copy link
Collaborator

jcblw commented Sep 10, 2015

I believe since we are not exporting out a key fordotenv the default import behavior of babel @maxbeatty mentions should work.

import dotenv from 'dotenv'
dotenv.config()

I would also be interested in how your running into an issue with preloading the module. Can you post the version of node/iojs and dotenv that you are using?

@Sparragus
Copy link
Author

@maxbeatty and @jcblw

I've uploaded a repo with sample code showing everything I mentioned on my first message.

All examples use [email protected], [email protected], and [email protected].

Clone it and then make sure you also pull all the branches. Here's a one-liner to do that.

Each branch has a README.md with details about that particular branch. Make sure to check it out.

Start by trying out the master branch. Install all the dependencies with npm install. Then run the app with npm start.

Then try the other two branches: preload-dotenv and import-dotenv-then-config.

Let me know how it goes.

@maxbeatty
Copy link
Contributor

babel-node has a -r option which conflicts with node's option of the same name. If you move the argument to after the script name, log outputs the first line but the second which depends on environment variables is undefined. If you change your start script to use babel/register, everything works as expected.

node -r dotenv/config -r babel/register index.js

This is an issue with using babel-node. Because it's primarily a development tool and not something that would be used in production, I'm inclined to close this issue.

@Sparragus
Copy link
Author

@maxbeatty I agree with you. I didn't know about babel-node providing a different -r than node. I also liked that run script better than the one I'm using. I'm using it and it works.

Thanks all for your comments.

@colllin
Copy link

colllin commented Feb 10, 2016

On newer version of babel (v6.x?), this is what worked for me:

  1. Add import 'babel-polyfill'; at the top of all app entrypoint files.

  2. At the comand line...

    $ npm install babel-polyfill babel-register --save
    $ node --require 'dotenv/config' --require 'babel-register' index.js
    

    Or using nodemon...

    $ nodemon index.js --exec "node --require 'dotenv/config' --require 'babel-register'"
    

I've also found that this way (manually importing babel-polyfill in your entrypoint files) is an easy way to ensure consistency between running your es6 files in development mode (using 'babel-register') and running in production mode (using babel to transpile during deployment). Otherwise we ran into issues where babel-node automatically included the babel-polyfill, but then running babel on the same file didn't automatically include babel-polyfill, so you could end up with broken code on production that worked fine in your dev environment. We tried importing babel-polyfill manually but then when running babel-node, we received errors for importing the polyfill twice.

walkerrandolphsmith referenced this issue in walkerrandolphsmith/VersionOne.Planr Aug 11, 2016
…red in a file not commited to repo.

Note nodemon is being run with node instead of babel-node because dotenv can be passed a required module from command line using node's -r flag which is highjacked by babel-node,
Therefore server/index.js is back to using babel-node inline.
@kachkaev
Copy link

kachkaev commented Nov 30, 2016

node --require 'dotenv/config' --require 'babel-register' index.js worked perfectly for me, thanks @colllin!

Just a small note for those want to develop on Windows: you should remove quotes around the module names:

node --require dotenv/config --require babel-register index.js
nodemon index.js --exec "node --require dotenv/config --require babel-register"

Using this in package.json:

{
  // ...
  "scripts": {
    // ...
    "start": "nodemon src/ --exec \"node --require dotenv/config --require babel-register\"",
    "start:prod": "babel-node --require dotenv/config --require babel-register src/"
  }
  // ...
}

@arvi
Copy link

arvi commented Jun 1, 2017

@Sparragus Thanks a lot. I was getting undefined on my dotenv config and changing it to import {} from 'dotenv/config'; worked for me. 😄

maxbeatty added a commit that referenced this issue Jul 8, 2017
* document trimming behavior of values during parsing closes #197

* make comments conform to jsdoc 3 fixes #201

* document options for working with imports ref #206 #133 #89

* add exampe of using returned object from config
@saeta-eth
Copy link

saeta-eth commented May 15, 2018

I have the following lines on the index.js file:

import dotenv from 'dotenv';
dotenv.config({ path: path.join(__dirname, '.env') });

console.log(process.env.MY_ENV_VAR) is a string value which is fine, but if I try the same in other file console.log(process.env.MY_ENV_VAR) is undefined.

I've added import {} from 'dotenv/config' to this other file and process.env.MY_ENV_VAR is now a string value.
It's really weird, but works for me.

Thanks @Sparragus.

@uds214125
Copy link

uds214125 commented Jun 7, 2018

You should follow this step as also mentioned in dotenv documentation.
#133

it is working fine in all the files.
0. in package.json
"start": "nodemon --require dotenv/config app.js --exec babel-register"

  1. create config.js or env.js with these contents
    import dotenv from 'dotenv'
    dotenv.config({ silent: true });

  2. in server.js/app.js or your root file.
    import _ from './config.js';

  3. check in another file
    console.log('env : ', process.env);

@djmattski
Copy link

djmattski commented Jun 26, 2018

For anyone using Babel - this saved me: https://www.npmjs.com/package/babel-plugin-dotenv

@onwuvic
Copy link

onwuvic commented Jun 28, 2018

@uds214125 Your method works fine for me. But I didn't have to preload the 'dotenv/config' on my script.

@kaleidoscope18
Copy link

@slorenzo Since I had the same weird behaviour as you, I found out it's because, as it has been quoted above :

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.

It means that the imports are executed first, so executes import dotenv from 'dotenv'; which is simply importing node_module/dotenv and all other imports including your other file that won't have the custom .env data.
Then it would execute dotenv.config({ path: path.join(__dirname, '.env') }); which fetchs the custom .env data.

When you put import {} from 'dotenv/config', it would immediately import the .env data.

To summarize :
1- import dotenv from 'dotenv' is executed first (if you put it before all imports, of course)
2- all other imports are made
3- fetch .env with dotenv.config({ path: path.join(__dirname, '.env') });

@bflemi3
Copy link

bflemi3 commented Apr 29, 2019

does this not work for you?

import 'dotenv/config`

@Alexzanderk
Copy link

does this not work for you?

import 'dotenv/config`

good job the best way to import, I don't know why on the top import to a variable!

@izznogooood
Copy link

does this not work for you?

import 'dotenv/config`

good job the best way to import, I don't know why on the top import to a variable!

Because this does not work in node v12.10.0

(node:19322) ExperimentalWarning: The ESM module loader is experimental.
internal/modules/esm/default_resolve.js:79
  let url = moduleWrapResolve(specifier, parentURL);
            ^

Error: Cannot find module /home/anders/dev/kaboom/node_modules/dotenv/config imported from /home/anders/dev/myfamily-api/src/index.mjs
    at Loader.resolve [as _resolve] (internal/modules/esm/default_resolve.js:79:13)
    at Loader.resolve (internal/modules/esm/loader.js:73:33)
    at Loader.getModuleJob (internal/modules/esm/loader.js:152:40)
    at ModuleWrap.<anonymous> (internal/modules/esm/module_job.js:43:40)
    at link (internal/modules/esm/module_job.js:42:36) {
  code: 'ERR_MODULE_NOT_FOUND'
}

@webhacking
Copy link

import * as dotenv from 'dotenv';
dotenv.config()

@Weffe
Copy link

Weffe commented Oct 7, 2019

Just in case anyone is using esm to get es module syntax, then you can combine it with dotenv like so:

node -r esm -r dotenv/config main.js

This preloads your dotenv values and ensures that your references to them in your es modules will work properly.

@naeem-gitonga
Copy link

import { config } from 'dotenv';
config();

@alex-r89
Copy link

import { config } from 'dotenv';
config();

The result of this is the env variables inside a parsed parent object, why is this?

IE if I console.log(config()) I get:

{
  parsed: {
    NODE_ENV: 'dev',
    DB_NAME: 'default',
    DB_TYPE: 'postgres',
    DB_HOST: 'localhost',
    ...rest of my .env file contents
  }
}

@AntonioGomes42
Copy link

I made a project, to show how it worked for me. https://github.com/AntonioGomes42/DotEnv-with-ES6-COMMOM-JS- . It was the only way i could find to make dotenv work with '.env' file an so. I hope it could help someone!

@MackKaputo
Copy link

import dotenv from "dotenv"
import path from "path"
const __dirname = path.resolve()
dotenv.config({ path: path.join(__dirname, './.env') })

@CherryDT
Copy link

@MackKaputo This has the same problem as explained above unless you put it into a separate module only for this purpose and then import that module at the top of your main module.

@Naibeye
Copy link

Naibeye commented Jan 5, 2022

For anyone using Babel - this saved me: https://www.npmjs.com/package/babel-plugin-dotenv

It is worked

@motdotla
Copy link
Owner

Hi everyone. We've updated the docs to clarify this for ESM. It is right at the top fold now:

https://github.com/motdotla/dotenv#2-as-early-as-possible-in-your-application-import-and-configure-dotenv

Screen Shot 2022-01-16 at 1 09 49 AM

@Hyzyr
Copy link

Hyzyr commented Feb 1, 2022

import dotenv from 'dotenv';

dotenv.config({path: 'config.env'});

@jeanbmar
Copy link

jeanbmar commented Mar 9, 2022

Hi everyone. We've updated the docs to clarify this for ESM. It is right at the top fold now:

https://github.com/motdotla/dotenv#2-as-early-as-possible-in-your-application-import-and-configure-dotenv

Screen Shot 2022-01-16 at 1 09 49 AM

How do you deal with custom path with this?

@ashen780
Copy link

ashen780 commented Apr 28, 2022

import 'dotenv/config'; console.log(process.env.DATABASE_HOST_TEST);

works for node version v16.14.2

@alexcroox
Copy link

All these examples that work across files don't seem to show how to set the config, e.g a custom path.

@joeflan
Copy link

joeflan commented Jun 28, 2022

@alexcroox One thing that worked for me (but I don't know if it's completely correct) to get the path working was:

import * as dotenv from 'dotenv'
dotenv.config({path:"custom/path/filename"})

@motdotla
Copy link
Owner

@alexcroox good point!

Yes, @joeflan is correct here. I'll update the README to communicate that better.

@bitdom8
Copy link

bitdom8 commented Jul 3, 2022

Path is default to ./root/.

To add path to current directory (Dir), we need this code inside of the file that we want all variables to be imported

import dotenv from 'dotenv'
import path from 'path'

import {fileURLToPath} from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
dotenv.config({ path: path.join(__dirname, '.env') });
// const express = require('express')
import express from 'express';

server.js and .env can should be in the same directory if you don't manipulate the path.join(__dirname.

You can reach the variables process.env.myvariable. Just be sure it works and print all variables: console.log(process.env)

Thanks

@jarodsim
Copy link

import { config } from 'dotenv'

@joshh71390
Copy link

now sure why so much stress import {config} from 'dotenv"

config()

@CherryDT
Copy link

CherryDT commented Dec 7, 2022

now sure why so much stress import {config} from 'dotenv"

config()

This will lead to surprises depending on how some modules import other modules, for the reasons I explained here: #89 (comment)

@hsuabina
Copy link

@SrBrahma This explains it all very well: https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/

Then think about it again - If you have files like this:

// main.js
import dotenv from 'dotenv'
dotenv.config()

console.log('MAIN', process.env.STUFF) 

import './sub.js'
// sub.js
console.log('SUB', process.env.STUFF)

... and you run STUFF=abc node main.js, then you will get this:

SUB undefined
MAIN abc

Remember this sentence from the article on the section about evaluation:

Just as with instantiation, this is done as a depth first post-order traversal.

Because for any module before itself is evaluated, any imported modules from top to bottom are evaluated (unless they already were evaluated elsewhere before), the imported module dotenv is evaluated first, and then sub is evaluated, and oops, dotenv.config() didn't run yet. Afterwards, now that all imported modules of main were evaluated, main itself can be evaluated, and dotenv.config() is called, but for the poor sub module that's already too late.

This is solved with import 'dotenv/config' because this loads the environment already as part of its own evaluation.

@CherryDT , is this correct though?

I just tested it and I get this output:

SUB abc
MAIN abc

@crizardevelop
Copy link

Don't try to add a name to the file (like variables.env). Just create a file with the name ".env"

image

image

@pujan789
Copy link

import 'dotenv/config';

@CherryDT
Copy link

CherryDT commented Oct 19, 2023

@SrBrahma This explains it all very well: https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/
Then think about it again - If you have files like this:

// main.js
import dotenv from 'dotenv'
dotenv.config()

console.log('MAIN', process.env.STUFF) 

import './sub.js'
// sub.js
console.log('SUB', process.env.STUFF)

... and you run STUFF=abc node main.js, then you will get this:

SUB undefined
MAIN abc

Remember this sentence from the article on the section about evaluation:

Just as with instantiation, this is done as a depth first post-order traversal.

Because for any module before itself is evaluated, any imported modules from top to bottom are evaluated (unless they already were evaluated elsewhere before), the imported module dotenv is evaluated first, and then sub is evaluated, and oops, dotenv.config() didn't run yet. Afterwards, now that all imported modules of main were evaluated, main itself can be evaluated, and dotenv.config() is called, but for the poor sub module that's already too late.
This is solved with import 'dotenv/config' because this loads the environment already as part of its own evaluation.

@CherryDT , is this correct though?

I just tested it and I get this output:

SUB abc
MAIN abc

You are right, I made a mistake while writing this post.

Of course it works because I incorrectly wrote to use STUFF=abc node main.js, so there isn't even any .env file involved!

What I meant to write (and changed now accordingly) was to use node main.js and creating a .env file with STUFF=abc in it.

@volodalexey
Copy link

Doesn't work for my case. I have nextjs and expressjs applications alognside and I want to start expressjs using .env.local from nextjs.
content index.ts:

import dotenv from "dotenv";
dotenv.config({
  path: resolve(process.cwd(), ".env.local"),
});
import { env } from "./env" // <-- throws zod parse error

content env.ts:

import z from "zod";
const configSchema = z.object({
  NEXTAUTH_URL: z.string().url(),
});
export const env = configSchema.parse(process.env);

The problem is because process.env inside index.ts is not the same as process.env inside env.ts.
Running index.ts as tsx index.ts (tsx).

Any ideas how to solve this?

@CherryDT
Copy link

CherryDT commented Nov 4, 2023

That's exactly the scenario I explained why import dotenv from 'dotenv'; dotenv.config() doesn't work and import 'dotenv/config' has to be used.

However, in your case you need to pass arguments to .config so you can't use that method. Instead, you can move your dotenv setup into a separate module and include that in your main module at the very top:

index.ts:

import './setupDotEnv';
import { env } from "./env";

setupDotEnv.ts:

import dotenv from "dotenv";
dotenv.config({
  path: resolve(process.cwd(), ".env.local"),
});

env.ts:

import z from "zod";
const configSchema = z.object({
  NEXTAUTH_URL: z.string().url(),
});
export const env = configSchema.parse(process.env);

@paraschouhan15
Copy link

import dotenv from 'dotenv'
import express from "express";
import colors from "colors"

dotenv.config();

const app = express();

app.get('/', (req, res) => {
res.send(
"

Welcome to my aapp

"
);
})
//port

const port = process.env.PORT

app.listen(port, () => {
console.log(Server running on mode ${process.env.DEV_MODE} port number ${port}.bgYellow.black)
})
I am getting undefined...in console PORT number AND developmnt mode

@luiskurihara
Copy link

does this not work for you?

import 'dotenv/config`

YOU SAVED ME THANKSSS

@motdotla
Copy link
Owner

motdotla commented Jan 19, 2024

for those coming across this we are starting to recommend using dotenvx

https://github.com/dotenvx/dotenvx

  • runs anywhere (cross-platform)
  • multiple enviornments
  • encrypted envs

dotenvx will work the same whether you are running node, bun, deno, python, anything.

@abhinavph21
Copy link

Doesn't work for my case. I have nextjs and expressjs applications alognside and I want to start expressjs using .env.local from nextjs. content index.ts:

import dotenv from "dotenv";
dotenv.config({
  path: resolve(process.cwd(), ".env.local"),
});
import { env } from "./env" // <-- throws zod parse error

content env.ts:

import z from "zod";
const configSchema = z.object({
  NEXTAUTH_URL: z.string().url(),
});
export const env = configSchema.parse(process.env);

The problem is because process.env inside index.ts is not the same as process.env inside env.ts. Running index.ts as tsx index.ts (tsx).

Any ideas how to solve this?

read this,
https://nextjs.org/docs/app/api-reference/next-config-js/env,
in nextjs, you have to create next.config.js for environment variables

@jonjieviduya
Copy link

I tried every single code here but it always says "process is not defined"

@GohilTushar
Copy link

import dotenv from "dotenv";
import path from 'path';
dotenv.config({
path:path.resolve(<Your path for .env>)
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests