Express.js app entry point - javascript

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());

Related

Why is the Router() Express function returning undefined?

I'm using TypeScript and Express to create an API that follows the principles of Clean Architecture. I separated each route of my application in a folder, and then I import all of them into an index.ts file, merging each one inside an unique router and exporting the same. My problem is that the importation of this unique file is returning undefined.
I think that is isn't a problem of my code itself, since TypeScript can infer correctly the type of the imported file and doesn't report any error during development. The only error I get just happens when trying to run the code:
TypeError: app.use() requires a middleware function
This is the code snippet that causes the error:
// expressInstance.ts --> Imported by the "server.ts" file, where listening the server happens
import express from "express";
import cors from "cors";
import { router } from ".."; // File with all the routes
const expressInstance = express();
expressInstance.use(express.urlencoded({extended: false}));
expressInstance.use(express.json());
expressInstance.use(cors());
expressInstance.use(router); // Line where the error appears
export {
expressInstance
};
And this is the code of the imported router file:
// index.ts
import { Router } from "express";
import { userRouter } from "./user.routes";
import { postRouter } from "./post.routes";
const router = Router();
router.use("/user", userRouter);
router.use("/post", postRouter);
export {
router
};
I read in another question that the undefined return used to happen at Express version 3, but I use the 4.17.13 version, so I think this isn't the cause of the problem. In fact, I have no idea what could be. I tried to see the router content by using console.log(router) in the index.ts file (before being imported by expressInstance.ts), but it didn't work, because the code wasn't even executed.
Try adding a path to your .use()
e.g:
expressInstance.use('/users', router); // in the case you wanted the router to be for users

Express.js returning multiple routes in a single exported router object returns incorrect route

I am trying to utilize an MVC pattern for express. I am modularizing routes and trying to declare the express router in only the server entry file and nowhere else.
My current issue is when exporting my appRoute function in my main routes file (see below), the first route (user route), returns users. I have another route called game that is exported in the same function but it still returns users instead of games.
Both routes have a controller function called getAll that gets different data from different tables.
If I try and visit the route: http://localhost:8000/user/getAll, it returns all users just fine.
If I try and visit the route: http://localhost:8000/game/getAll, it still returns all users even when they're different routes...
If I were to flip the order of users and games in the main routes file where game is first and user is second, users starts to return games. It's like the second route mimics the first route.
This may be something simple, but any help I will appreciate.
My code is as shown below
server entry point (index.js)
const app = express();
const router = express.Router();
const bootstrap = require('./src/bootstrap');
bootstrap(app, router);
I am passing on the app and router instance to my bootstrap file where all routes will be exported to.
bootstrap.js (this file gets all exported routes and uses them within the app)
const { appRoute } = require('./routes');
module.exports = (app, router) => {
return app.use('/', appRoute(router));
};
I am passing on the router instance to my main routes file where all routes are exported from.
Main routes file (index.js): this file requires all routes and uses them within the router instance. I think this might be where my issue is but I am a little stuck on how I might fix it.
const { userRoute } = require('./userRoute');
const { gameRoute } = require('./gameRoute');
exports.appRoute = (router) => {
router.use('/user', userRoute(router));
router.use('/game', gameRoute(router));
return router;
};
Game route files (index.js): returns all users instead of games
const { gameController } = require('../../controllers');
exports.gameRoute = (router) => {
return router.get('/getAll', gameController.getAll);
};
Any help is greatly appreciated. If there is any clarification needed please let me know.
I think you need to create a separate router for game and user.
See the express.Routing section and birds.js example here

Nodejs how can I add another main app.js and use both of them to make my code clean?

Hello I'm using nodejs and express framework and I wrote all my serverside code into my app.js file but it's little bit complicated for me cause cause I have almost 250 line code and I want to implement authentication now so I want to create another app.js to write my code only for auth so other code will not confuse me how can I do that????
UPDATED
as you se above I have 2 post requests from my app.js I asking that how I will get thoso request in my auth.js file cause as I understand exports import for static js field I want to take request and save that information my database and I want to that in my auth.js file
Here are a simplified structure for your project derived from my project at this link.
routes.js:
Create a file named route.js where you define all the routes for your application. In this case the routes will be only the register and the login routes that will be handled by the UserController module.
import { Router } from 'express';
import UserController from './UserController';
const router = Router();
router.post(
'/register',
UserController.register
);
router.post(
'/login',
UserController.login
);
export default router;
UserController.js:
This file/class that handle all the operations for the creation and login of an user
As you can see all the methods has no route url because they are called directly from our route.js file. We are dividing and structuring your application!
export default class UserController {
public static register(req, res) {
// Register operation
}
public static login(req, res) {
// Login operation
}
}
app.js
The entry point and where you are configuring your express application.
import express from 'express';
import routes from './routes';
const app = express();
// configure app ...
// Here we attach our routes url to the express app
app.use('/', routes)
Hope it helps :)
depending on your setup you can divide your files in per example:
app.js
authentication.js
and then either require or import functions from the authentication.js file into app.js like this:
import express from "express";
or this:
const express = require("express");
functions inside the authentication file should be exported like this:
//needs to be imported as this: import {authenticate} from "authenticate";
export function authenticate(){};
//needs to be imported as this: import authenticate from "authenticate";
function authenticate(){};
export default authenticate;
or this:
// needs to be imported as this: const authenticate = require("authenticate");
module.exports = function authenticate(){};
See this guide on enabling ES6 imports as shown above. this has the preference for it can save on memory when importing.

Is it possible to use application level middleware in express router?

I've been playing around with setting up a basic atlassian-connect-express (ACE) application, and have modified the starter code provided by the ACE package to be suitable for serverless deployment. One of the problems I faced after doing this was that routing is now divided into stages, e.g. /dev, /prod. I did a bit of research and found that a way to deal with this would be to use an express Router and mount it to the appropriate endpoint for the stage being deployed to. The problem I then faced is that the authentication middleware provided by ACE seems to be application level and then can't be used by each router.
Typically the routes were added to the express app like this:
import ace from 'atlassian-connect-express';
import express from 'express';
import routes from './routes';
const app = express();
const addon = ace(app);
app.use(addon.middleware());
routes(app, addon);
and in ./routes/index.js
export default function routes(app, addon) {
// Redirect root path to /atlassian-connect.json,
// which will be served by atlassian-connect-express.
app.get('/', (req, res) => {
res.redirect('/atlassian-connect.json');
});
// This is an example route used by "generalPages" module (see atlassian-connect.json).
// Verify that the incoming request is authenticated with Atlassian Connect.
app.get('/hello-world', addon.authenticate(), (req, res) => {
// Rendering a template is easy; the render method takes two params:
// name of template and a json object to pass the context in.
res.render('hello-world', {
title: 'Atlassian Connect'
});
});
// Add additional route handlers here...
}
I've changed ./routes/index.js to work as a router object and export that, however this leaves me unable to use the addon.authenticate() middleware
import ace from 'atlassian-connect-express';
import express from 'express';
import routes from './routes';
const app = express();
const addon = ace(app);
app.use('/dev', require('./routes'));
and in ./routes/index.js
const express = require('express');
const router = express.Router();
// Redirect root path to /atlassian-connect.json,
// which will be served by atlassian-connect-express.
router.get('/', (req, res) => {
res.redirect('/atlassian-connect.json');
});
// This is an example route used by "generalPages" module (see atlassian-connect.json).
// Verify that the incoming request is authenticated with Atlassian Connect.
router.get('/hello-world', addon.authenticate(), (req, res) => {
// Rendering a template is easy; the render method takes two params:
// name of template and a json object to pass the context in.
res.render('hello-world', {
title: 'Atlassian Connect'
});
});
module.exports = router;
Obviously having no knowledge of addon, the router cannot use that authentication middleware.
Is it possible to pass that middleware through to the router when attaching it to the application? If not, is there another way I can handle URL prefixes without using a router?

Node.js unable to import ES6 module

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()
}

Categories

Resources