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.
Related
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();
I am experiencing a weird error. I was just creating a new folder to separate my routes.
Inside I created the routes using the express.Router API and I exported the router itself.
Here is how the code looks like inside post-routes.ts:
import express from 'express';
const router = express.Router();
router.get( '/', ( req, res ) =>
{
res.send( 'This is working' );
} );
export default router;
Ok. All I have to do now is to import it inside my index.ts and create a root route for my post-routes.ts file.
Please tell me if you see something wrong in my approach.
index.ts file:
import express from 'express';
import cors from 'cors';
import postRouter from './routes/post-routes';
const PORT = process.env.PORT || 5000;
app.use( '/posts', postRouter );
app.use( express.json( { limit: '30mb' } ) );
app.use( express.urlencoded( { limit: '30mb', extended: true } ) );
app.use( cors() );
app.listen( PORT, () => console.log( `listening at ${ PORT }` ) );
Good. Now to offer as much information as I possibly can, I will insert the following:
File structure inside my project:
package.json file configuration:
As you can see, I am using the "type": "module" to be able to use import inside my node application. As for the typescript, I am getting no compiling errors, so index.ts sees the post-routes.ts file so I would expect that index.js would see post-routes.js file as well. But the server throws the error below. How can this be?
Error [ERR_MODULE_NOT_FOUND]: Cannot find module 'E:\Github\hospytal-fasion\server\dist\routes\post-routes' imported from E:\Github\hospytal-fasion\server\dist\index.js
Apart from making sure that target and module are set to esnext in your tsconfig.json, make sure to include file extensions in your imports. i.e. try changing the following line:
import postRouter from './routes/post-routes';
to
import postRouter from './routes/post-routes.js';
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()
}
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.
It appears some route middleware is running before index.js, which is crashing my application because the order of execution prevents me from loading my dotenv file on time.
I would like to load my dotenv file before everything in order to make sure that all modules that require it will have access to it.
However, while debugging this issue, I noticed that a console.log at the top of the app entry point still doesn't log first.
Folder structure:
src
------index.js
middleware
------auth.js
routes
------auth.route.js
------index.js
.env
Code that logs first middleware/auth.js:
import { Strategy as JwtStrategy, ExtractJwt } from 'passport-jwt'
module.exports = function() {
console.log('this prints first with undefined', process.env.JWT_SECRET) <-------
which gets called in auth.route.js
import auth from '../middleware/auth'
const userRouter = express.Router()
userRouter.get('/dashboard', auth().authenticate(), function(req, res) {
res.send('Authenticated, user id is: ' + req.user.id)
})
index.js file:
console.log("this prints second"); <---------
(...)
import routes from './routes'
import express from 'express'
import auth from './middleware/auth'
require('dotenv').config({ silent: process.env.NODE_ENV === 'production' })
const app = express();
(...)
app.use('/api', routes);
It's not clear from the code fragments in your question where the problem, because I think you've omitted the place where you import the auth.route.js module.
The answer that follows assumes that you actually import auth.route.js in the index.js file, probably before importing middleware/auth (which isn't actually used in the code fragment you posted). So, based on that assumption, here's what's probably happening:
The thing to keep in mind is that code in a module is executed when it is imported. This doesn't affect middleware/auth at first, since all it does is declare a function. But it does affect auth.route.js - look at what that module does:
It imports the auth function from middleware/auth module, which calls the userRouter.get() function. One of the arguments to that function is a call to the auth() function.
If you're importing auth.route.js in your code (and I suspect you are), then that's where the auth function is getting called prematurely.
Now, how to fix this? It's actually pretty easy: the auth.route.js file should export a function which is called to initialize the routes when you're ready, like this:
auth.route.js
import auth from '../middleware/auth'
const userRouter = express.Router()
export default () => {
userRouter.get('/dashboard', auth().authenticate(), function(req, res) {
res.send('Authenticated, user id is: ' + req.user.id)
})
return userRouter;
}
And when you're ready to initialize the routes (ie. when everything else is set up).
import getRouter from './modules/auth.route.js';
app.use('/api', getRouter());