I have a db.js file like so
import "dotenv/config";
const devConfig = `"${process.env.PG_USER}://${process.env.PG_USER}:${process.env.PG_PASSWORD}#${process.env.PG_HOST}:${process.env.PG_PORT}/${process.env.PG_DATABASE}"`;
const prodConfig = process.env.DATABASE_URL;
const dbConnection =
process.env.NODE_ENV === "production" ? prodConfig : devConfig;
console.log(dbConnection);
export default dbConnection;
The console.log function correctly outputs the string that I need (which is "postgres://postgres:learn#sql#localhost:5432/EPL" in my case)
However, when I am trying to use import the dbConnection variable in another file, all I get is "undefined://undefined:undefined#undefined:undefined/undefined".
The code of the file that I am trying to use the dbConnection variable is like below.
import pgPromise from "pg-promise";
import dbConnection from "../db.js";
const pgp = pgPromise({});
const db = pgp(dbConnection);
console.log(dbConnection);
What am I doing wrong?
By default, dotenv looks for the .env file in the current working directory, ie the directory you are in when running your app...
Path
Default: path.resolve(process.cwd(), '.env')
If you run node query.js, then your CWD is whatever directory query.js lives in and dotenv will look for .env in that directory.
If you want to force dotenv to look in the same directory as your db.js file, the ES module version looks like this...
// db.js
import { dirname, resolve } from "path";
import { fileURLToPath } from "url";
import dotenv from "dotenv";
dotenv.config({
path: resolve(dirname(fileURLToPath(import.meta.url)), ".env"),
});
// create and export your connection strings...
If you weren't using ES modules, it would look more like this
const { resolve } = require("path");
require("dotenv").config({ path: resolve(__dirname, ".env") });
Can you try this
import dotenv from "dotenv";
//Write this as early as possible in your application
//This will use the local .env file.
dotenv.config();
Related
I'm trying to import a route.js file in my index.js file. I am exporting in the routes file and importing it in the index with .js extension. What am I missing here?
Folder structure:
Routes folder is in the server folder, same as index.js - accessible by a single . VsCode also highlights the location when typing, so that's not the issue.
routes.js:
import { registerUser } from '../controllers/userController'
const router = express.Router()
router.post("/", registerUser)
export default router;
index.js:
import mongoose from "mongoose";
import cors from "cors";
import dotenv from "dotenv";
import userRoutes from './routes/userRoutes.js'
dotenv.config();
const app = express();
app.use(cors());
app.use('/users', userRoutes)
const PORT = process.env.PORT;
mongoose
.connect(process.env.CONNECTION_URL)
.then(() => console.log("DB Connected"))
.then(() =>
app.listen(PORT, () => console.log(`Server is running on ${PORT}`))
)
.catch((error) => console.log(error.message));
On the fourth import in the index.js file try removing the ".js" so it looks like this:
import userRoutes from './routes/userRoutes'
...if that file exists.
try changing from
import userRoutes from './routes/userRoutes.js';
to
import userRoutes from './routes/routes.js';
If that doesn't work, can you share output of tree command or ls command in your root directory.
As far as I am concerned. the .js extension would not be an issue in the import statement. So, if the naming part is okay. It's better to share error code with folder and naming structure in the question as well.
I'm trying to separate my routes to a separate module in routes.js and then importing in app.js. I'm getting a lot of errors in the console.
internal/modules/esm/default_resolve.js:96
let url = moduleWrapResolve(specifier, parentURL);
^
Error: Cannot find module /Users/rhoxh/Desktop/24/routes imported from /Users/rhoxh/Desktop/24/app.js
at Loader.resolve [as _resolve] (internal/modules/esm/default_resolve.js:96:13)
at Loader.resolve (internal/modules/esm/loader.js:73:33)
at Loader.getModuleJob (internal/modules/esm/loader.js:147:40)
at ModuleWrap.<anonymous> (internal/modules/esm/module_job.js:41:40)
at link (internal/modules/esm/module_job.js:40:36) {
code: 'ERR_MODULE_NOT_FOUND'
}
routes.js
import express from 'express';
const router = express.Router();
router.get('/', (req, res) => {
res.send('home page');
});
export default router;
app.js
import express from 'express';
import { router } from './routes';
const app = express();
const PORT = 8080;
app.listen(PORT, () => {
console.log(`Server running at: http://localhost:${PORT}/`);
});
// Routes
app.use('/', router);
What am I doing wrong here?
You need to use the full file name:
import router from './routes.js';
From the documentation:
module-name
The module to import from. This is often a relative or
absolute path name to the .js file containing the module. Certain
bundlers may permit or require the use of the extension; check your
environment. Only single quoted and double quoted Strings are allowed.
You can check this link it could help you https://github.com/nodejs/node/issues/27408
You can try to use --es-module-specifier-resolution=node as it says.
For anyone looking for a typescript solution use the .js file extension of your transpiled files.
// index.ts file.
import router from './routes.js';
You are destructuring your import yet you are exporting as default.
When you import a default there is no need for destructuring
import router from './routes';
You can use destructuring when you are using either a named export
export const router = express.Router()
or you pull out a property from the default export
export default {
router: express.Router()
}
I am wanting to create a config file containing environment variables. I don't want this file to be tracked by git so my solution is to create a default javascript config file e.g. config.default.js and have the other developers copy the contents of this file into a config.js file and modify the variables for their local environments.
The problem is if the config.js file does not exist yet when the import it to read the config file is run I get 'unable to resolve path to module' error. Is there a way to import config.js if it exists but import config.default.js if it doesn't?
You could use a simple try{}catch(){} when you require the file.
try {
var myconf = require('./config.js');
} catch (error) {
var myconf = require('./config.default.js');
}
You could use fs module and check if configuration file exists and require accordingly.
let fs = require('fs');
let configFile = './config';
let defaultConfigFile = './config.default.js';
let myconf = null;
if (fs.existsSync(configFile)) {
myconf = require(configFile);
} else {
myconf = require(defaultConfigFile);
}
Background
Let's say you have an ES2015/ES6 component with a single function as a default export:
component.js
export default function() {
console.log("Hello, World!");
}
An app to include it in:
app.js
import myComponent from "./component"
myComponent();
And an Node.js/Express server using webpack-dev-middleware, to run Webpack (with Babel) on and serve app.js, and to also include component.js in:
server.js
const express = require("express");
const app = express();
// start the server
app.listen(process.env.EXPRESS_PORT, () => {
console.log(`App listening on port ${process.env.EXPRESS_PORT}!`);
});
const webpack = require("webpack");
const webpackConfig = require("./webpack.config");
const compiler = webpack(webpackConfig);
// use webpack-dev-middleware to serve the publicPath
app.use(
require("webpack-dev-middleware")(compiler, {
logLevel: "warn",
publicPath: webpackConfig.output.publicPath,
})
);
// use webpack-hot-middleware for HMR
app.use(require("webpack-hot-middleware")(compiler));
const myComponent = require("./component") // import the component
myComponent(); // use the component
Question
How might you go about using component.js in both server.js, as well as in the webpacked app.js?
Problem
As-is, that component works fine in app.js, but throws a SyntaxError: Unexpected token export in the Node console when trying to do const component = require("./component").
Since Babel is only running via Webpack, and server.js is accessing component.js via the original component and not the bundled/transpiled one, we get the error.
I suppose a solution would be to run Babel twice: once on the component before the server.js is started, and once again in Webpack, but that seems very inelegant and inefficient.
I seem to have stumbled across a working solution: write the module in CommonJS format, and Webpack/Babel will compile it for ES6.
Working files:
component.js
function myComponent() {
console.log("Hello, World!");
}
module.exports = { myComponent };
app.js
import myComponent from "./component"
myComponent();
server.js
const { myComponent } = require("./component") // import the component
myComponent(); // use the component
Trying to use an environment variable from my dotenv file crashes my app because it seems the dotenv file is not loaded on time.
// server.js
require('dotenv').config({ silent: process.env.NODE_ENV === 'production' })
console.log("Here is the env var: ", process.env.SPARKPOST_API_KEY) // works
import express from 'express'
import routes from './routes'
(...)
app.use('/api', routes);
// routes/index.js
import somecontroller from '../controllers/somecontroller'
const routes = express.Router()
routes.use('/somecontroller', somecontroller)
// somecontroller.js
import sendEmail from '../helpers/sparkpost'
// ./helpers/sparkpost.js
import SparkPost from 'sparkpost'
var sparky = new SparkPost() // uses process.env.SPARKPOST_API_KEY
export default function sendEmail() {
}
crashes with error
Error: Client requires an API Key.
The API key is there, so it seems that sparkpost.js is instantiating new SparkPost() before the dotenv file gets loaded.
How do I work around this?
Javascript imports are hoisted (but not Typescript!), so imported modules will initialize before any of the current modules initialization code gets to run. Fortunately imported modules are initialized in order, so a possible workaround is putting the config code in its own module:
// main.js <- make this your entry point
import "./config";
import "./server";
// config.js
import dotenv from "dotenv";
dotenv.config({ silent: process.env.NODE_ENV === 'production' });
// server.js
import express from 'express';
import SparkPost from 'sparkpost';
const sparky = new SparkPost();
...
Edit:
Even simpler:
// config.js
import dotenv from "dotenv";
dotenv.config({ silent: process.env.NODE_ENV === 'production' });
// server.js <- make this your entry point
import './config';
import express from 'express';
import SparkPost from 'sparkpost';
const sparky = new SparkPost();
...
Edit:
The original question and answer was written with only Javascript in mind. Years have passed, and Typescript became popular. The Typescript compiler currently does not hoist imports, so you could just initialize dotenv right in the entrypoint .ts file, before the imports.