0

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?

Razvan Zamfir
  • 4,209
  • 6
  • 38
  • 252
  • in rounting override, `.use()` = all http verbs, `.get()` forces to only use GET verb... just change `app.get('/dashboard', ...)` to `app.use('/dashboard', ...)` as in your rounting you are specifing the `router.get` already ... `app.get('/dashboard', ...)` would only be valid if the function you pass return just `(res, req) => { ... }` but you are retunging a `Router` type – balexandre Feb 04 '20 at 09:59
  • @balexandre I get `router is not defined` error. – Razvan Zamfir Feb 04 '20 at 10:06
  • I've created [this code](https://github.com/balexandre/so59977566) this week for an answer, check how I did it and will work :) - you might even learn a thing or two! let us know how it went – balexandre Feb 04 '20 at 10:09

11 Answers11

1

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}!`));
Fathma Siddique
  • 266
  • 3
  • 15
1

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);
Razvan Zamfir
  • 4,209
  • 6
  • 38
  • 252
Sai Kumar
  • 287
  • 1
  • 5
1

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).

Vinay Shrestha
  • 266
  • 2
  • 8
1

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}!`));
Mazen
  • 486
  • 4
  • 8
1

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:

Zeeshan Hassan Memon
  • 8,105
  • 4
  • 43
  • 57
  • Please help me with **[this](https://stackoverflow.com/questions/60548245/express-js-application-bug-req-validationerrors-method-does-not-work)** issue. There is a BOUNTY for it too. Thank you! – Razvan Zamfir Mar 12 '20 at 11:24
1

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);
Abishek Kumar
  • 519
  • 5
  • 13
0

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

properchels
  • 260
  • 2
  • 20
0

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.

Titus Sutio Fanpula
  • 3,467
  • 4
  • 14
  • 33
  • 1
    I operated the changes, but the problem remained. – Razvan Zamfir Feb 04 '20 at 10:12
  • Only change it in your `index.js`, not in `dashboard.js`. – Titus Sutio Fanpula Feb 04 '20 at 12:07
  • 1
    I need to keep the code for the posts too: `// Bring the Posts Routes const postsRoute = require('./routes/front-end/posts'); // Get Posts app.use('/', postsRoute); // Get Single Post app.use('/:id', postsRoute);`. As soon as I do that, the dashboard becomes inaccessible. – Razvan Zamfir Feb 04 '20 at 12:43
  • I can't solve your `posts` route, because you're not provide it in your question, so I don't know where's the problem and how to fix it. But for dashboard, there's an example and if you follow the dashboard, then its will working fine. – Titus Sutio Fanpula Feb 04 '20 at 12:45
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/207180/discussion-between-titus-sutio-fanpula-and-razvan-zamfir). – Titus Sutio Fanpula Feb 04 '20 at 12:51
0

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

Serhiy Mamedov
  • 1,080
  • 5
  • 11
0

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

0

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;
  • I have pushed all the code I currently have to **[GitHub](https://github.com/Ajax30/XPressBlog)**. Have a look. Thanks! – Razvan Zamfir Feb 05 '20 at 14:58