Razvan Zamfir
Razvan Zamfir

Reputation: 4686

Express.js application bug: the browser is stuck in loading state when trying to access certain route

I am working on a blogging application (click the link to see the GitHub repo) with Express, EJS and MongoDB.

For a reason I have been unable to identify, when (in the browser) I try to go to: http://localhost:3000/dashboard it (the browser) get stuck in a loading state and never actually loads the dashboard route.

In the "entry" index.js file I have:

const express = require('express');
const dotenv = require('dotenv');
const mongoose = require('mongoose');
const path = require('path');
const morgan = require('morgan');
const expressLayouts = require('express-ejs-layouts');
const app = express();

dotenv.config();

//Conect to MONGODB
mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => {
    console.log('conected');
});

mongoose.connection.on('error', err => {
    console.log(`DB connection error: ${err.message}`);
});

// Set static directory
app.use(express.static(path.join(__dirname, 'public')));

// Set views directory
app.set('views', path.join(__dirname, 'views'));

// Set view engine
app.set('view engine', 'ejs');

// Use Express Layouts
app.use(expressLayouts);

// Middleware
app.use(morgan('dev'));

// Bring the Posts Routes
const postsRoute = require('./routes/front-end/posts');

// Get Posts
app.use('/', postsRoute);

// Get Single Post
app.use('/:id', postsRoute);

// Bring the Dashboard
const dashboardRoute = require('./routes/admin/dashboard');

// Get Dashboard
app.use('/dashboard', dashboardRoute);

const port = process.env.PORT || 3000;

app.listen(port, () => console.log(`Listening on port ${port}!`));

In the dashboard route (routes\admin\dashboard.js) I have:

const express = require('express');
const dashboardController = require('../../controllers/admin/dashboard');

// Express router
const router = express.Router();

// Dysplay Dashboard
router.get('/dashboard', dashboardController.displayDashboard);

module.exports = router;

While in the dashboard controller:

const Post = require('../../models/post');

exports.displayDashboard = (req, res, next) => {
    res.send('Dashboard');
};

in routes/front-end/posts.js I have:

const express = require('express');
const postsController = require('../../controllers/front-end/posts');

// Express router
const router = express.Router();

// Get Posts
router.get('/', postsController.getPosts);

// Get Single Post
router.get('/:id', postsController.getSinglePost);

module.exports = router;

The posts controller:

const Post = require('../../models/post');

exports.getPosts = (req, res, next) => {
    const posts = Post.find({}, (err, posts) => {
        if(err){
            console.log('Error: ', err);
        } else {
            res.render('default/index', {
                layout: 'default/layout',
                website_name: 'MEAN Blog',
                page_heading: 'XPress News',
                page_subheading: 'A MEAN Stack Blogging Application',
                posts: posts
            });
        }
    });
};

exports.getSinglePost = (req, res, next) => {
    let id = req.params.id;
    if (id.match(/^[0-9a-fA-F]{24}$/)) {
        Post.findById(id, function(err, post){
            if(err){
                console.log('Error: ', err);
            } else {
                res.render('default/singlepost', {
                    layout: 'default/layout',
                    website_name: 'MEAN Blog',
                    post: post
                });
            }
        });
    }
};

IMPORTANT: It is necessary that every single post is displayed under the root url, for example: http://localhost:3000/5e3063dbfa749d9229bab26f where 5e3063dbfa749d9229bab26f is, of course the post id.

This is for SEO purposes. I intent to later replace id with post slug: http://localhost:3000/my-great-post.

How can I achieve this?

Upvotes: 0

Views: 4790

Answers (11)

Abishek Kumar
Abishek Kumar

Reputation: 539

You only need to add postRoute once in index.js,

// Bring the Dashboard
const dashboardRoute = require("./routes/admin/dashboard");
// Get Dashboard
app.use('/dashboard', dashboardRoute);
// Bring the Posts Routes
const postsRoute = require('./routes/front-end/posts');
// Get Posts
app.use('/', postsRoute);

and in the dashboardRoute file change the route to '/' instead of '/dashboard', else you need to use localhost:3000/dashboard/dashboard to get the dashboard

router.get('/', dashboardController.displayDashboard);

Upvotes: 1

Zeeshan Hassan Memon
Zeeshan Hassan Memon

Reputation: 8325

FIXED IT, for complete running example clone node-cheat XPressBlog and run node index followed by npm i.

Point browser to http://localhost:3000/dashboard, outputs:

This is Dashboard

http://localhost:3000, outputs:

This will load all posts; continue with your logic!

http://localhost:3000/my-great-post, outputs:

This will load single post with slug : my-great-post

What you were doing wrong?

You were confusing the use of app.use(, so those are fixed as per your needs (as mentioned in your post) like this:

const dashboardRoute = require("./routes/admin/dashboard");
app.use('/dashboard', dashboardRoute);

const postsRoute = require('./routes/front-end/posts');
app.use('/', postsRoute);

In case you wish to explore more about app.use here are the links:

Upvotes: 1

Mazen
Mazen

Reputation: 556

Your routing issue could be fixed with replacing the order of /:id and /dashboard.

const express = require('express');
const dotenv = require('dotenv');
const mongoose = require('mongoose');
const path = require('path');
const morgan = require('morgan');
const expressLayouts = require('express-ejs-layouts');
const app = express();

dotenv.config();

//Conect to MONGODB
mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => {
    console.log('conected');
});

mongoose.connection.on('error', err => {
    console.log(`DB connection error: ${err.message}`);
});

// Set static directory
app.use(express.static(path.join(__dirname, 'public')));

// Set views directory
app.set('views', path.join(__dirname, 'views'));

// Set view engine
app.set('view engine', 'ejs');

// Use Express Layouts
app.use(expressLayouts);

// Middleware
app.use(morgan('dev'));

// Bring the Posts Routes
const postsRoute = require('./routes/front-end/posts');

// Get Posts
app.use('/', postsRoute);

///////////////////////UPDATED PART/////////////////////////////////////
// Bring the Dashboard
const dashboardRoute = require('./routes/admin/dashboard');

// Get Dashboard
app.use('/dashboard', dashboardRoute);

// Get Single Post
app.use('/:id', postsRoute);
////////////////////////////////////////////////////////////////////////


const port = process.env.PORT || 3000;

app.listen(port, () => console.log(`Listening on port ${port}!`));

Upvotes: 1

Vinay Shrestha
Vinay Shrestha

Reputation: 266

You are not configuring your routes correctly in your index.js file. Try this:

const express = require("express");
const dotenv = require("dotenv");
const mongoose = require("mongoose");
const path = require("path");
const morgan = require("morgan");
const expressLayouts = require("express-ejs-layouts");
const app = express();

dotenv.config();

//Conect to MONGODB
mongoose
  .connect(process.env.MONGO_URI, {
    useNewUrlParser: true,
    useUnifiedTopology: true
  })
  .then(() => {
    console.log("conected");
  });

mongoose.connection.on("error", err => {
  console.log(`DB connection error: ${err.message}`);
});

// Set static directory
app.use(express.static(path.join(__dirname, "public")));

// Set views directory
app.set("views", path.join(__dirname, "views"));

// Set view engine
app.set("view engine", "ejs");

// Use Express Layouts
app.use(expressLayouts);

// Middleware
app.use(morgan("dev"));

// Bring the Posts Routes
const postsRoute = require('./routes/front-end/posts');

const dashboardRoute = require("./routes/admin/dashboard");

// Get Dashboard
app.use('/dashboard', dashboardRoute);

// Get Posts
app.use('/', postsRoute);

const port = process.env.PORT || 8080;

app.listen(port, () => console.log(`Example app listening on port ${port}!`));

You don't need to use two postsRoutes. Check this: https://expressjs.com/en/guide/routing.html

Also I suggest you add your post route like so: app.use('/post, postsRoute).

Upvotes: 1

Sai Kumar
Sai Kumar

Reputation: 285

In Index.js you are adding routes exports to "/" , in express routes paths will match with regular expression. so when added router to "/" so everything will be start from here.

// Bring the Posts Routes
const postsRoute = require('./routes/front-end/posts');

// Get Posts
app.use('/', postsRoute);

// Get Single Post
app.use('/:id', postsRoute);

// Bring the Dashboard
const dashboardRoute = require('./routes/admin/dashboard');

// Get Dashboard
app.use('/dashboard', dashboardRoute);

Here what is happing is First now route become like this

/ you added postsRoutes

So now routes becomes

/ - will gives all posts /:id - will gives single post

Again you added postsRoutes to ":/id" /:id - postsRoutes

so now routes becomes

/:id will gives all posts /:id/:id will give single posts

So you have to remove any one line from those

FINE this one only

// Get Posts
app.use('/', postsRoute);

And for your dashboard you are done same thing

app.use('/dashboard', dashboardRoute);

now routes becomes

/dashboard/dashboard - it will give dashboard

but this one override the "/:id/:id" routing matching so everthing now override by this one

so create another route for getting the posts like app.use("/posts", postsRoute);

/posts -> it will give all posts

/posts/:id -> it will give single info


And dashboard routes you need to change

router.get('/dashboard', dashboardController.displayDashboard);

/dashboard -> "/"

router.get('/', dashboardController.displayDashboard);

Final routes will be

const postsRoute = require('./routes/front-end/posts');

// Get Posts
app.use('/posts', postsRoute);

// Get Single Post
// THIS ONE WILL COMMENT 
// app.use('/posts/:id', postsRoute);

// Bring the Dashboard
const dashboardRoute = require('./routes/admin/dashboard');

// Get Dashboard
app.use('/dashboard', dashboardRoute);

In dashboard routes

router.get('/', dashboardController.displayDashboard);

Upvotes: 1

Fathma Siddique
Fathma Siddique

Reputation: 264

Firstly, app.use('/:id', postsRoute); and app.use('/dashboard', dashboardRoute); are same to the browser. Cause when the browser gets '/dashboard', it doesn't know whether the string 'dashboard' is a id or not. Because of that, it gets stuck as the URL indicates to both of these routes. So change the app.use('/:id', postsRoute); to app.use('/post/:id', postsRoute); . This will work fine.

Secondly, according to your code the URL should be http://localhost:3000/dashboard/dashboard, not http://localhost:3000/dashboard.

index.js

const express = require('express');
const dotenv = require('dotenv');
const mongoose = require('mongoose');
const path = require('path');
const morgan = require('morgan');
const expressLayouts = require('express-ejs-layouts');
const app = express();

dotenv.config();

//Conect to MONGODB
mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => {
    console.log('conected');
});

mongoose.connection.on('error', err => {
    console.log(`DB connection error: ${err.message}`);
});

// Set static directory
app.use(express.static(path.join(__dirname, 'public')));

// Set views directory
app.set('views', path.join(__dirname, 'views'));

// Set view engine
app.set('view engine', 'ejs');

// Use Express Layouts
app.use(expressLayouts);

// Middleware
app.use(morgan('dev'));

// Bring the Posts Routes
const postsRoute = require('./routes/front-end/posts');

// Get Posts
app.use('/', postsRoute);

// Get Single Post
app.use('/post/:id', postsRoute); //****changed******

// Bring the Dashboard
const dashboardRoute = require('./routes/admin/dashboard');

// Get Dashboard
app.use('/dashboard', dashboardRoute);

const port = process.env.PORT || 3000;

app.listen(port, () => console.log(`Listening on port ${port}!`));

Upvotes: 1

Evgeniy Grabelsky
Evgeniy Grabelsky

Reputation: 156

You need to reorganize your routes. Your app architecture looks confusing. If you are using express Router then you need to do something like this:

your index.js

...
const routes = require('./routes/index');
... 
// Middleware
app.use(morgan('dev'));

app.use('/', routes);

const port = process.env.PORT || 3000;

you need to create routes/index.js file containing something like this:

const express = require('express');
const router = express.Router();
const dashboardController = require('../controllers/admin/dashboard');
const postsController = require('../controllers/front-end/posts');

// Get Posts
router.get('/posts', postsController.getPosts);

// Get Single Post
router.get('/posts/:id', postsController.getSinglePost);

// Display Dashboard
router.get('/dashboard', dashboardController.displayDashboard);

module.exports = router;

Upvotes: 0

Ashot Arzumanyan
Ashot Arzumanyan

Reputation: 151

try to write app.get('/:id', postsRoute); after app.get('/dashboard', dashboardRoute);

when http://localhost:3000/dashboard url called it will call postRoute route instead of dashboardRoute, because express will recognize '/dashboard' as '/:id', so req.params.id should be equal to 'dashboard' inside postRoute

Upvotes: 0

Serhii Mamedov
Serhii Mamedov

Reputation: 1343

You're applying 2 filters.

First in app.get('/dashboard', dashboardRoute); and then in router.get('/dashboard', dashboardController.displayDashboard);.

So you're probably creating http://localhost:3000/dashboard/dashboard route

Try removing filter from one of them

Upvotes: 0

Titus Sutio Fanpula
Titus Sutio Fanpula

Reputation: 3613

The only one reason is because you're using get twice. First in your index.js and the second one in your dashboard.js. You should not to do that.

So, to fix it, make sure in your index.js don't use app.get:

app.get('/dashboard', dashboardRoute);

Only use app.use:

// Get Dashboard
app.use('/dashboard', dashboardRoute);

After you use set app.use('/dashboard') in your index.js, make sure in your dashboard.js, like this code below:

const express = require('express');
const dashboardController = require('../../controllers/admin/dashboard');

// Express router
const router = express.Router();

// Dysplay Dashboard
router.get('/', dashboardController.displayDashboard);

module.exports = router;

Now, you can call your endpoint with url: localhost:3000/dashboard.

For an example of your code, you can look at my codesanbox: https://codesandbox.io/s/express-ejs-bug-answer-0nyo9

I hope it can help you.

Upvotes: 0

properchels
properchels

Reputation: 260

in your entry file index.js , you are registering the routes in the wrong way.

To register routes, you have to use app.use() and not app.get()

Hence change app.get('/', postsRoute); to app.use('/', postsRoute);

Let me know if this worked.

Edit: after looking again I saw you are adding the prefix "dashboard" twice to the route.

So change the line: app.get('/dashboard', dashboardRoute);

to

app.use('/', dashboardRoute);

Upvotes: 0

Related Questions