Error: Route.get() requires a callback function but got a [object Object] [duplicate] - express

So I'm starting to use Node.js. I saw the video with Ryan Dahl on Nodejs.org and heard he recommended Express-js for websites.
I downloaded the latest version of Express, and began to code. I have a fully fledged static view up on /, but as soon as I try sending parameters, I get errors like this:
Cannot GET /wiki
I tried following the guide on expressjs.com but the way one uses routes has changed in the latest version, which makes the guide unusable.
Guide:
app.get('/users/:id?', function(req, res, next){
var id = req.params.id;
if (id) {
// do something
} else {
next();
}
});
Generated by Express:
app.get('/', routes.index);
My problem arises when I try and add another route.
app.get('/wiki', routes.wiki_show);
I've tried a bunch of approaches, but I keep getting the Cannot GET /wiki (404) error.
routes/index.js looks like this:
exports.index = function(req, res) {
res.render('index', { title: 'Test', articles: articles, current_article: current_article, sections: sections })
};
The only thing I did there was add some parameters (arrays in the same file) and this i working. But when I copy the contents and change exports.index to exports.wiki or exports.wiki_show I still get the Cannot GET /wiki error.
Can anyone explain to me what I'm missing here? - Thanks.

So, after I created my question, I got this related list on the right with a similar issue: Organize routes in Node.js.
The answer in that post linked to the Express repo on GitHub and suggests to look at the 'route-separation' example.
This helped me change my code, and I now have it working. - Thanks for your comments.
My implementation ended up looking like this;
I require my routes in the app.js:
var express = require('express')
, site = require('./site')
, wiki = require('./wiki');
And I add my routes like this:
app.get('/', site.index);
app.get('/wiki/:id', wiki.show);
app.get('/wiki/:id/edit', wiki.edit);
I have two files called wiki.js and site.js in the root of my app, containing this:
exports.edit = function(req, res) {
var wiki_entry = req.params.id;
res.render('wiki/edit', {
title: 'Editing Wiki',
wiki: wiki_entry
})
}

The route-map express example matches url paths with objects which in turn matches http verbs with functions. This lays the routing out in a tree, which is concise and easy to read. The apps's entities are also written as objects with the functions as enclosed methods.
var express = require('../../lib/express')
, verbose = process.env.NODE_ENV != 'test'
, app = module.exports = express();
app.map = function(a, route){
route = route || '';
for (var key in a) {
switch (typeof a[key]) {
// { '/path': { ... }}
case 'object':
app.map(a[key], route + key);
break;
// get: function(){ ... }
case 'function':
if (verbose) console.log('%s %s', key, route);
app[key](route, a[key]);
break;
}
}
};
var users = {
list: function(req, res){
res.send('user list');
},
get: function(req, res){
res.send('user ' + req.params.uid);
},
del: function(req, res){
res.send('delete users');
}
};
var pets = {
list: function(req, res){
res.send('user ' + req.params.uid + '\'s pets');
},
del: function(req, res){
res.send('delete ' + req.params.uid + '\'s pet ' + req.params.pid);
}
};
app.map({
'/users': {
get: users.list,
del: users.del,
'/:uid': {
get: users.get,
'/pets': {
get: pets.list,
'/:pid': {
del: pets.del
}
}
}
}
});
app.listen(3000);

Seems that only index.js get loaded when you require("./routes") .
I used the following code in index.js to load the rest of the routes:
var fs = require('fs')
, path = require('path');
fs.readdirSync(__dirname).forEach(function(file){
var route_fname = __dirname + '/' + file;
var route_name = path.basename(route_fname, '.js');
if(route_name !== 'index' && route_name[0] !== "."){
exports[route_name] = require(route_fname)[route_name];
}
});

You could also organise them into modules. So it would be something like.
./
controllers
index.js
indexController.js
app.js
and then in the indexController.js of the controllers export your controllers.
//indexController.js
module.exports = function(){
//do some set up
var self = {
indexAction : function (req,res){
//do your thing
}
return self;
};
then in index.js of controllers dir
exports.indexController = require("./indexController");
and finally in app.js
var controllers = require("./controllers");
app.get("/",controllers.indexController().indexAction);
I think this approach allows for clearer seperation and also you can configure your controllers by passing perhaps a db connection in.

No one should ever have to keep writing app.use('/someRoute', require('someFile')) until it forms a heap of code.
It just doesn't make sense at all to be spending time invoking/defining routings. Even if you do need custom control, it's probably only for some of the time, and for the most bit you want to be able to just create a standard file structure of routings and have a module do it automatically.
Try Route Magic
As you scale your app, the routing invocations will start to form a giant heap of code that serves no purpose. You want to do just 2 lines of code to handle all the app.use routing invocations with Route Magic like this:
const magic = require('express-routemagic')
magic.use(app, __dirname, '[your route directory]')
For those you want to handle manually, just don't use pass the directory to Magic.

Related

express-validator on PUT methods

I'm creating an API and decided to use express-validator for validation (duh), I've never used this before so I'm unsure on some aspects of it so my validations might not be the best but I'm getting by.
I have built two validation middle ware using this and export them from the same folder like this:
module.exports = {
create: require('./create'),
update: require('./update')
}
So I can then do this in my router:
const validation = require('../validations/plotValidation')
// ...
router.get('/', controller.all)
router.post('/create', validation.create(), controller.create)
router.get('/:plotId', controller.read)
router.put('/:plotId/update', validation.update(), controller.update)
router.delete('/:plotId/delete', controller.delete)
// ...
I'm not good enough with express-validator to do both validate both routes with the same file, maybe I'll try it at some point, anyway.
The .post method works fine and validates everything I want it to however the .put method just seems to be ignoring every check here are is file in case you want to see the checks:
const { body, check, param } = require('express-validator');
module.exports = () => {
return [
param('plotId')
.exists().withMessage('URI requires plot id'),
body('price')
.optional()
.isObject()
]
}
As you can probably tell I only just stated it, but even with only these two tiny checks it just doesn't seem to run.
Does express-validator not work on PUT methods?
For anyone else who has this issue I solved this by using .run on my checks, you can read more about this here essentially this is the code that saved me:
// parallel processing
const validate = validations => {
return async (req, res, next) => {
await Promise.all(validations.map(validation => validation.run(req)));
const errors = validationResult(req);
if (errors.isEmpty()) {
return next();
}
res.status(400).json({ errors: errors.array() });
};
};

Having trouble with pathing my express-handlebars

Here is the layout of the relevant folders in my current project:
package.json
develop
server.js
routes
htmlRoutes.js
controllers
userControl.js
views
home.handlebars
profile.handlebars
layouts
main.handlebars
The URL I'm trying to go to is: '/users/profile/:id'
htmlRoutes.js is linked correctly to my server.js
My htmlRoutes.js imports a function from userControl.js (in controllers)
My userControls.js is where I have my code that's supposed to take the user to their profile.
Relevant lines of code:
Server.js
app.use('/', require('./routes/htmlRoutes'))
app.engine('handlebars', exphbs({
layoutsDir: (path.join(__dirname, '/views/layouts')),
defaultLayout:'main'}
))
htmlRoutes.js
const { takeUserToProfile } = require('../controllers/userControl')
// this one actually works
router.get('/', (req, res) => {
res.render(path.join(__dirname, '../views/home'))
})
router.get('/users/profile/:id', takeUserToProfile)
userControl.js
// this one gives me an error
exports.takeUserToProfile = async (req, res) => {
const data = await User.findById(req.params.id)
res.render('profile', {
username: data.username,
firstName: data.firstName,
lastName: data.lastName
})
}
I tried many different things and all I get is an error like this:
Error: Failed to lookup view "profile" in views directory "C:\Users\User\Desktop\repoName\views"
Is there any way to tell my file that there's a 'develop' folder between my repoName and views?

add middleware function to an async export

I have separated my routes with their definitions so that my routes look like this:
const router = require('express').Router();
const handle = require('../handlers');
router.post('/register', handle.register);
// The handler defines the route. Like this:
exports.register = async (req, res, next) => {
try {
const user = await db.User.create(req.body);
const {id, username} = user;
res.status(201).json({id, username});
} catch (err) {
if(err.code === 11000){
err.message ='Sorry, details already taken';
}
next(err);
}
};
This works fine. Now I need to upload images as users register (using multer middleware). A lot of the examples show multer is used like this:
router.post('/register', upload ,function (req, res, next) { //upload is the multer middleware
console.log(req.file);
})
How do I add the middleware in my case?
You add the middleware like this:
router.post('/register', upload, handle.register);
Just like the examples you see.
In My Not-So-Humble Opinion
You mention that you have separated your routes from your route handler. In my opinion this is not only misguided but completely wrong.
In the usual MVC architecture you see in other languages, the route and the route handler together makes the controller. In your design you have split your controller into two separate structures. Primarily, this makes your route handling logic and the information of what route it does it for exist in two separate files when they should be located very close to each other.
Now that you are adding middlewares you are finding out that middleawares are installed in routes and with your design it is hard to figure out what middleware is installed for your route handler.
The correct way to separate your business logic from your routes is to follow the MVC design pattern - separate your Model from your Controller (the View is free because it is basically res.json()).
In my not-so-humble opinion you should be creating a model for your user instead of separating controllers into two parts. Your user obviously need a register functionality and the register function should just succeed or fail but should not concern itself with any HTTP error handling because that is the controller's job:
exports.user = {
// .. other methods
register: async function (username, password) {
try {
const user = await db.User.create({username, password});
return user;
} catch (err) {
if(err.code === 11000){
err.message ='Sorry, details already taken';
}
throw err;
}
}
}
Then in your controller (most Express example call "route") you do:
const user = require('./user');
router.post('/register', upload , async (req, res, next) => {
try {
const user = user.register(req.body.username, req.body.password);
const {id, username} = user;
res.status(201).json({id, username});
} catch (err) {
next(err);
}
})
However, this is just my personal opinion.

How do I expose my initialized bookshelf object to my routes file?

As this is my first foray into nodejs/express/bookshelf please be gentle and verbose if possible with regards to your comments/answers.
I'm having a hard time figuring out how to properly use express with bookshelf, specifically, with exposing objects in the various modules. The bookshelf docs say that ideally the initialization should only happen once and that the initialized bookshelf instance should be returned 'throughout your library'.
In my app.js file I create a knex/bookshelf connection to my database, and I also define my model mapping to the table I want..
(app.js)
var knex = require('knex')({
client: 'sqlite3',
connection: {
filename: '<path-to-my-db'
}
});
...
var questionRoutes = require('./routes/questions');
var app = express();
var bookshelf = require('bookshelf')(knex);
// define model
var Question = bookshelf.Model.extend({
tableName: 'questions'
});
...
app.use('/', routes);
app.use('/api', questionRoutes);
In my routing file i want to pull in data using Bookshelf...
(routes/quesions.js)
var express = require('express');
var router = express.Router();
// on routes that end in /questions ...
router.get('/questions', function (req, res) {
new Question()
.fetchAll()
.then(function (questions) {
console.log(questions.toJSON());
});
});
...but how do I expose my Question model object to my routing file? Or, alternatively, if I moved the model definition to my routes, then how do I expose my initialized bookshelf instance?
I haven't been able to find any useful reference apps for Bookshelf, just code snippets and API docs.
I'm learning node.js and related stuff as well; I would have done as follows:
lib/common/bookshelf.js sets up Bookshelf.js:
var dbConfig = {
client: 'sqlite3'
// ...
};
var knex = require('knex')(dbConfig);
var bookshelf = require('bookshelf')(knex);
module.exports = bookshelf;
lib/model/question.js file to define the Question model:
var bookshelf = require("../common/bookshelf");
var Question = bookshelf.Model.extend({
tableName: 'questions'
});
lib/routes/question.js:
var Question = require('../model/question');
// rest of the code.
I can't remember where I found this but I using a "make" function to support passing in supporting singleton helpers (like bookshelf). The make function is exported and then I call it whenever I include the route file. See the following example:
index.js (main file)
var passport = require('passport');
var knex = require('knex')({
client: 'mysql',
connection: {
host : 'localhost',
user : 'user',
password : 'password',
database : 'database',
charset : 'utf8'
}
});
var bookshelf = require('bookshelf')(knex);
require('./routes/user')(app, bookshelf);
user.js route file
module.exports = function(app, bookshelf) {
app.get('/user/id/:id', function (req, res) {
User = require('../models/user').make(bookshelf);
... // do something
});
}
UPDATE
I just ended up refactoring the above code into a supporting file I call datastore and then I assign it as an attribute of the app.
// datastore.js
module.exports = function (app) {
app.set('datastore', bookshelf);
return bookshelf;
};
// app.js
var datastore = require('./lib/datastore')(app);
app.set('datastore', datastore);
So far both options worked for me but I went with the latter trying to refactor more code of app.js.

Use external function in express.js middleware

How can I access some function from the outside world in the middleware? For example, if I need to check some part of request and set locals.
app.use(function (req, res, next) {
if (myfunc(req)) { // <-- how should I load this function to be accessible here?
res.locals.myvar = true;
}
next();
});
At the head of that file you'd want to import a custom module:
var myfunc = require('./path/to/myfunc/module')
That file would look like:
var myfunc = function(req) {
// Do something.
}
module.exports = myfunc;
Then, you should just be able to call: myfunc(req);
Further information on Custom NodeJS Modules.