I am writing my own app (both of back and frontend). I want to ask you guys if I am doing it in the right way.
I want to split server.js to a few files (in PHP I would use include() for it) but I am not sure if it is the right way.
Here is some code example:
const app = require('express')(),
fs = require('fs'),
http = require('http').Server(app),
io = require('socket.io')(https),
path = require('path'),
login_user = require('./routes/login_user'),
add_user = require('./routes/add_user'),
logout = require('./routes/logout');
app.post('/login_user', (req, res, next) => {
login_user.go(req, res, next);
});
app.post('/add_user', (req, res) => {
add_user.go(req, res);
});
app.get('/logout', (req, res) => {
logout.go(req, res);
});
Please note that's not the whole code and I want to focus on spliting "routes" or "paths" to a few files. For example a whole API login system is in /routes/login_user.js file (exported).
Now I have got so many paths and code is looking a little bit weird.
app.post('/check_balance', (req, res) => {
check_balance.go(req, res);
});
app.post('/add_incoming', (req, res) => {
add_incoming.go(req, res);
});
app.post('/add_outgoing', (req, res) => {
add_outgoing.go(req, res);
});
app.post('/add_category', (req, res) => {
add_category.go(req, res);
});
app.post('/change_settings', (req, res) => {
change_settings.go(req, res);
});
app.post('/login_user', (req, res, next) => {
login_user.go(req, res, next);
});
app.post('/add_user', (req, res) => {
add_user.go(req, res);
});
app.post('/verify_user', (req, res) => {
verify_user.go(req, res);
});
app.get('/logout', (req, res) => {
logout.go(req, res);
});
app.get('/check_settings', (req, res) => {
check_settings.go(req, res);
});
app.get('/check_categories', (req, res) => {
check_categories.go(req, res);
});
app.post('/remove_categories', (req, res) => {
remove_categories.go(req, res);
});
app.get('/check_incomings', (req, res) => {
check_incomings.go(req, res);
});
app.get('/check_outgoings', (req, res) => {
check_outgoings.go(req, res);
});
app.get('/check_both', (req, res) => {
check_both.go(req, res);
});
app.get('/check_outgoings_chart', (req, res) => {
check_outgoings_chart.go(req, res);
});
app.get('/check_incomings_chart', (req, res) => {
check_incomings_chart.go(req, res);
});
app.post('/remove_incomings', (req, res) => {
remove_incomings.go(req, res);
});
app.post('/remove_outgoings', (req, res) => {
remove_outgoings.go(req, res);
});
Make your server.js as simple as possible and extract all your routing logic to separate folder (possibly name it "routes"). If you also want to define yours schema, put it in separate folder ("models"). A complete solution can be like this:
in Model Folder:
user.js
const mongoose = require("mongoose"); // In case if you want to use MongoDB
const userSchema = new mongoose.Schema({
name: { type: String, required:true },
email: { type: String, required: true },
password: { type: String, required: true },
});
exports.User = User;
In routes folder:
users.js
const { User } = require("../models/user");
const router = express.Router();
//define your routes here
router.get('/', async(req,res)=>{
const users = await User.find();
res.send(users)
});
module.exports = router;
And finally in your server.js
const app = require('express')(),
fs = require('fs'),
http = require('http').Server(app),
io = require('socket.io')(https),
path = require('path'),
users = require('./routes/users');
app.use("/api/users", users); //With this API Endpoint you can access it like http://{your_domain}/api/users
If you want to make it more clean, you can wrap all routing paths to another folder. Lets call it "startup".
with this you can do like this.
in your startup folder:
routes.js
const users = require("../routes/users");
module.exports = function(app) {
app.use("/api/users", users);
//add all routes here
}
Then in your server.js
require("./startup/routes")(app); //all routes will be imported
I think this is what you need.
Let's say that there is a file called routers/login.js:
var express = require('express');
var router = express.Router();
router.get('/login', function(req, res) {
// do something
});
then app.js:
...
var login = require('./routes/login');
app.use("/login", login)
Put all the routes files in a folder with multiple files like User_routes.js can contain routes related to user and so on.
Also all you need to then is to export these routes from each file by using module.export.your_module and include them in your server file like in user routes :
// Login Page
router.get('/login', (req, res) => res.render('login'));
// Register Page
router.get('/register',(req, res) => {
res.render('register'); });
then import it as
module.exports = router;
also include it as :
app.use('/users', require('./routes/users.js'));
app.use('/',(req,res,next)=>{
console.log('I should handle it Now.');
res.render('404');
});
Related
I am trying to create a login page and sign up page, my app.js gives me this error, I think it is the last line of this code. I can send you the other components(files) for this express app. I cannot understand what is causing this error.
const express = require('express');
const mongoose = require('mongoose');
// Routes
const authRoutes = require('./routes/authRoutes');
const app = express();
// middleware
app.use(express.static('public'));
app.use((err, req, res, next) => {
res.locals.error = err;
res.status(err.status);
res.render('error');
});
// view engine
app.set('view engine', 'ejs');
// database connection
const dbURI = '<database, username and password>';
mongoose.connect(dbURI, { useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex:true })
.then((result) => app.listen(3000))
.catch((err) => console.log(err));
// routes
app.get('/', (req, res) => res.render('home'));
app.get('/smoothies', (req, res) => res.render('smoothies'));
app.use(authRoutes);
authRoutes.js
const { Router } = require('express')
const authController = require('./authController.js')
const router = Router();
router.get('/signup', authController.signup_get);
router.get('/signup', authController.signup_post);
router.get('/login', authController.login_get);
router.get('/login', authController.login_post);
module.export = router;
authController.js
module.exports.signup_get = (req, res) => {
res.render('signup');
}
module.exports.login_get = (req, res) => {
res.render('login');
}
module.exports.signup_post = (req, res) => {
res.send('signup');
}
module.exports.login_post = (req, res) => {
res.send('login');
}
You are exporting incorrectly in authRoutes.js.
Change this:
module.export = router;
to this:
module.exports = router;
FYI, a little debugging on your own by simply doing a console.log(authRoutes) should have been able to show you where to look for the problem. If you get an error when you attempt to use authRoutes, you look at what it is and where it came from to see why it's not working. This is basic debugging and there is an expectation that you've done basic debugging before you post your question here.
I have the problem on routers. My main route /weather working, but others routers on him don't.
app.js
const express = require('express');
const weatherRoute = require('./back/routes/weatherRouter.js');
const app = express();
app.use(bodyParser.json());
app.disable('etag');
app.use('/weather', weatherRoute);
weatherRouter.js
const router = express.Router();
router.get('/', async (req, res) => {
try {
const wholeData = await WeatherInfo.find();
res.json(wholeData);
} catch (err) {
res.json({ message: err })
}
});
router.get('/now', (req, res) => {
res.send("ITS NOT WORKING");
});
module.exports = router;
the problem is that localhost:5000/weather working perfect, but when I want to use some other routers on that Route e.g. localhost:5000/weather/now that's not working
Any ideas what I'm doing wrong ?
UPDATED :
it works, when between those routers is no others routers.
e.g.
router.get('/', async (req, res) => {
//working
}
router.post('/:add', async (req, res) => {
//working
}
router.get('/now', async (req, res) => {
//doesnt work
}
If I move /now above /add router it works perfect. Can someone explain why is this happening ?
Define actual path in path section likerouter.post('/weather/now', (re, res) => {
//Handel re
}
I found the solution.
The routers position is matter. Reference to explanation
My last router didn't work, because another router already catched him.
app.get('/:add', function (req, res) {
// this will match all /a, /b .. including /new
res.end('done!');
});
app.get('/now', function (req, res) {
// this is never called
res.end('done!!');
});
What I am trying to do is simple training server app with CRUD functions.
I succeeded with that I can edit or delete already existing users, but stuck on trying to solve how actually to create and add to existing users.json file another one. I am sure that I am missing something.
Git repo for full directory: https://gitlab.com/alexgnatrow/practice-nodejs
Here is the main index.js.
const express = require('express');
const app = express();
const path = require('path');
const fs = require('fs');
const _ = require('lodash');
const engines = require('consolidate');
const bodyParser = require('body-parser');
let users = [];
function getUser(username) {
let user = JSON.parse(fs.readFileSync(getUserFilePath(username), {encoding: 'utf8'}));
user.nickname = user.name.toLowerCase().replace(/\s/ig, '');
return user
}
function getUserFilePath(username) {
return `${path.join(__dirname, 'users', username)}.json`
}
function createUser(username, data){
let fp = getUserFilePath(username);
let string = JSON.stringify(data, null , 2);
console.log(string);
fs.writeFileSync(fp, string , {encoding: 'utf8'})
}
function saveUser(username, data) {
let fp = getUserFilePath(username);
fs.unlinkSync(fp);
console.log(data);
fs.writeFileSync(fp, JSON.stringify(data, null, 2), {encoding: 'utf8'})
}
function verifyUser(req, res, next) {
let username = req.params.username;
let fp = getUserFilePath(username);
fs.exists(fp, yes => {
if (yes) {
next()
} else {
res.redirect('/error/' + username)
}
})
}
app.engine('hbs', engines.handlebars);
app.set('views', './views');
app.set('view engine', 'hbs');
app.use(express.static('public')); //example of serve static files
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json()); // Body parser use JSON data
app.get('/', (req, res) => {
fs.readdir('users', (err, files) => {
if(err|| files==="favicon.ico"){console.log('there is no need to create a fucking favicon');}
files = _.filter(files, file => !file.startsWith('.'));
users = _.map(files, file => getUser(file.replace(/\.json/ig, '')));
res.render('index', {users})
});
});
app.get('*.json', (req, res) => res.download('./users/' + req.path));
app.get('/error/:username', (req, res) => res.status(404).send(`No user named ${req.params.username} found`));
app.get('/data/:username', (req, res) => {
res.header("Content-Type", 'application/json');
res.send(JSON.stringify(getUser(req.params.username), null, 4));
});
app.all('/:username', function(req, res, next) {
console.log(req.method, 'for', req.params.username);
next()
});
app.get('/:username', verifyUser, function(req, res) {
const user = getUser(req.params.username);
res.render('user', {user, address: user.location})
});
app.post('/', (req,res) => {
createUser(req.params.name, req.body);
console.log(req.body);
res.end()
});
app.put('/:username', function(req, res) {
saveUser(req.params.username, req.body);
res.end()
});
app.delete('/:username', function(req, res) {
fs.unlinkSync(getUserFilePath(req.params.username)); // delete the file
res.sendStatus(200)
});
const server = app.listen(3000, function() {
console.log('Server running at http://localhost:' + server.address().port)
});
the index.hbs with html page where i am trying to add a user:
const express = require('express');
const app = express();
const path = require('path');
const fs = require('fs');
const _ = require('lodash');
const engines = require('consolidate');
const bodyParser = require('body-parser');
let users = [];
function getUser(username) {
let user = JSON.parse(fs.readFileSync(getUserFilePath(username), {encoding: 'utf8'}));
user.nickname = user.name.toLowerCase().replace(/\s/ig, '');
return user
}
function getUserFilePath(username) {
return `${path.join(__dirname, 'users', username)}.json`
}
function createUser(username, data){
let fp = getUserFilePath(username);
let string = JSON.stringify(data, null , 2);
console.log(string);
fs.writeFileSync(fp, string , {encoding: 'utf8'})
}
function saveUser(username, data) {
let fp = getUserFilePath(username);
fs.unlinkSync(fp);
console.log(data);
fs.writeFileSync(fp, JSON.stringify(data, null, 2), {encoding: 'utf8'})
}
function verifyUser(req, res, next) {
let username = req.params.username;
let fp = getUserFilePath(username);
fs.exists(fp, yes => {
if (yes) {
next()
} else {
res.redirect('/error/' + username)
}
})
}
app.engine('hbs', engines.handlebars);
app.set('views', './views');
app.set('view engine', 'hbs');
app.use(express.static('public')); //example of serve static files
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json()); // Body parser use JSON data
app.get('/', (req, res) => {
fs.readdir('users', (err, files) => {
if(err|| files==="favicon.ico"){console.log('there is no need to create a favicon');}
files = _.filter(files, file => !file.startsWith('.'));
users = _.map(files, file => getUser(file.replace(/\.json/ig, '')));
res.render('index', {users})
});
});
app.get('*.json', (req, res) => res.download('./users/' + req.path));
app.get('/error/:username', (req, res) => res.status(404).send(`No user named ${req.params.username} found`));
app.get('/data/:username', (req, res) => {
res.header("Content-Type", 'application/json');
res.send(JSON.stringify(getUser(req.params.username), null, 4));
});
app.all('/:username', function(req, res, next) {
console.log(req.method, 'for', req.params.username);
next()
});
app.get('/:username', verifyUser, function(req, res) {
const user = getUser(req.params.username);
res.render('user', {user, address: user.location})
});
app.post('/', (req,res) => {
createUser(req.params.name, req.body);
console.log(req.body);
res.end()
});
app.put('/:username', function(req, res) {
saveUser(req.params.username, req.body);
res.end()
});
app.delete('/:username', function(req, res) {
fs.unlinkSync(getUserFilePath(req.params.username)); // delete the file
res.sendStatus(200)
});
const server = app.listen(3000, function() {
console.log('Server running at http://localhost:' + server.address().port)
});
the error log
Server running at http://localhost:3000
TypeError: Path must be a string. Received undefined
at assertPath (path.js:28:11)
at Object.join (path.js:501:7)
at getUserFilePath (C:\Users\Лёша\jsDir\practice-nodejs\index.js:21:20)
at createUser (C:\Users\Лёша\jsDir\practice-nodejs\index.js:25:14)
at app.post (C:\Users\Лёша\jsDir\practice-nodejs\index.js:88:5)
at Layer.handle [as handle_request] (C:\Users\Лёша\jsDir\practice-nodejs\nod
e_modules\express\lib\router\layer.js:95:5)
at next (C:\Users\Лёша\jsDir\practice-nodejs\node_modules\express\lib\router
\route.js:137:13)
at Route.dispatch (C:\Users\Лёша\jsDir\practice-nodejs\node_modules\express\
lib\router\route.js:112:3)
at Layer.handle [as handle_request] (C:\Users\Лёша\jsDir\practice-nodejs\nod
e_modules\express\lib\router\layer.js:95:5)
at C:\Users\Лёша\jsDir\practice-nodejs\node_modules\express\lib\router\index
.js:281:22
at Function.process_params (C:\Users\Лёша\jsDir\practice-nodejs\node_modules
\express\lib\router\index.js:335:12)
at next (C:\Users\Лёша\jsDir\practice-nodejs\node_modules\express\lib\router
\index.js:275:10)
at C:\Users\Лёша\jsDir\practice-nodejs\node_modules\body-parser\lib\read.js:
130:5
at invokeCallback (C:\Users\Лёша\jsDir\practice-nodejs\node_modules\raw-body
\index.js:224:16)
at done (C:\Users\Лёша\jsDir\practice-nodejs\node_modules\raw-body\index.js:
213:7)
at IncomingMessage.onEnd (C:\Users\Лёша\jsDir\practice-nodejs\node_modules\r
aw-body\index.js:273:7)
In this chunk of code, req.params.name is undefined :
app.post('/', (req,res) => {
createUser(req.params.name, req.body);
console.log(req.body);
res.end()
});
req.params are the parameters passed to a route. (eg. /:name)
Perhaps you meant req.body (the JSON body in the POST payload)?
app.post('/', (req,res) => {
createUser(req.body.name, req.body);
console.log(req.body);
res.end()
});
I'm currently building an app and I have a routes file that looks like this.
const router = require('express').Router();
router.get('/', (req, res) => res.render('statics/home'));
router.get('/jobs', (req, res) => res.render('statics/jobs'));
router.get('/about-page', (req, res) => res.render('statics/about-page'));
router.get('/valves', (req, res) => res.render('statics/valves'));
router.all('*', (req, res) => res.notFound());
module.exports = router;
I am trying to figure out a way to refactor my routes and have a single route that accepts any string and then checks to see if a file exists matching it
Any help appreciated!
To easily handle static file, you can use express static, express will automatic route all file inside static folder
app = require('express')();
app.use('statics',express.static('statics'));
Something like this could work:
const router = require('express').Router();
const fs = require('fs');
router.get(':template', (req, res) => {
const tpl = req.param('template');
if (tpl) {
if (fs.existsSync('/path/to/templates/' + tpl + '.ext')) { // adjust the path and template extension
res.render('statics/' + tpl);
} else {
res.notFound();
}
} else {
res.render('statics/home');
}
});
router.all('*', (req, res) => res.notFound());
module.exports = router;
Or probably better approach would be to read the templates directory once and create routes based on its contents:
const router = require('express').Router();
const fs = require('fs');
const templates = fs.readdirSync('/path/to/templates');
templates.forEach(tpl => {
tpl = tpl.substring(tpl.lastIndexOf('/') + 1);
if (tpl === 'home') {
router.get('/', (req, res) => res.render('statics/home'))
} else {
router.get('/' + tpl, (req, res) => res.render('statics/' + tpl))
}
});
router.all('*', (req, res) => res.notFound());
module.exports = router;
I'm using:
express 4.14
node 7.0+
session 1.14+
I created Nodejs project with webstorm:
app.js
var express = require('express');
var session = require('express-session');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var index = require('./routes/index');
var user = require('./routes/user');
var app = express();
var check = function() {
!req.session.status ? res.redirect('/user/login') : next();
}
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(session({secret: "inline", resave: false, saveUninitialized: true, status: false}));
app.use('/', check, require('./routes/index'));
app.use('/user', require('./routes/user'));
module.exports = app;
index.js
var express = require('express');
var router = express.Router();
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
module.exports = router;
user.js
var express = require('express');
var router = express.Router();
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
router.get('/login', function(req, res, next) {
res.render('login');
});
module.exports = router;
When I request to localhost:3000, the page prompt me that "too many redirects', now the url is localhost:3000/user/login?
What's my problem?
fix:
var check = function(req, res, next) {
!req.session.status ? res.redirect('/user/login') : next();
}
But I'll write more beautiful solution for You, hope You'll get in idea:
app.js:
app.use(require('./routes'));
routes folder:
routes/
|- index.js
|- common/
|- auth.js
|- root/
|- index.js
|- posts.js
|- backend/
|- index.js
|- posts.js
...
1) routes/index.js :
const
express = require('express'),
router = express.Router();
// guarded routes
function guardFn(req, res, next) {
let authenticated = req.session && req.session.authenticated === true;
authenticated ? next() : res.redirect('/auth');
}
router.use('/backend', guardFn, require('./backend'));
// public routes
router.use('/auth', require('./common/auth'); // for auth purposes
router.use('/', require('./root')); // for routes that starts from /, try not to rewrite /backend, /auth
module.exports = router;
2) routes/common/auth :
const
express = require('express'),
router = express.Router();
mongoose = require('mongoose'),
User = mongoose.model('user');
router.get('/', (req, res) => {
res.render('common/auth');
});
router.post('/', (req, res) => {
User
.findOne({
username: req.body.username,
password: req.body.password
})
.exec((err, user) => {
if(err) {
console.error(err);
return res.status(500).send('System error! Try again later');
}
if(!user) return res.redirect('back');
req.session.user = user._id;
req.session.authenticated = true;
res.redirect('/backend');
});
});
function logout(req, res, next) {
delete req.session.user;
req.session.authenticated = false;
next();
}
router.delete('/auth', logout, (req, res) => res.send({success: true}));
router.get('/auth/destroy', logout, res => res.redirect('/auth'));
module.exports = router;
3) routes/root/index.js :
const
express = require('express'),
router = express.Router();
router.get('/', (req, res) => {
res.render('site/welcome');
});
module.exports = router;
4) routes/root/posts.js :
const
express = require('express'),
router = express.Router(),
mongoose = require('mongoose'),
Post = mongoose.model('post');
router.get('/', (req, res) => {
Post
.find()
.skip((req.query.page-1)*10)
.limit(10)
.exec((err, posts) => {
res.render('site/posts/list', {posts});
});
});
router.get('/:id', (req, res) => {
Post
.findById(req.params.id)
.exec((err, post) => {
if(err) {
console.error(err);
return res.status(500).send('System error! Try again later');
}
res.render('site/posts/show', {post});
});
});
module.exports = router;
5) routes/backend/index.js :
const
express = require('express'),
router = express.Router();
router.get('/', (req, res) => {
res.render('backend/dashboard');
});
module.exports = router;
6) routes/backend/posts.js :
const
_ = require('lodash'),
express = require('express'),
router = express.Router(),
mongoose = require('mongoose'),
Post = mongoose.model('post');
router.get('/', (req, res) => {
Post
.find()
.skip((req.query.page-1)*50)
.limit(50)
.exec((err, posts) => {
res.render('backend/posts/list', {posts});
});
});
router.get('/:id', (req, res) => {
Post
.findById(req.params.id)
.exec((err, post) => {
if(err) {
console.error(err);
return res.status(500).send('System error! Try again later');
}
res.render('backend/posts/show', {post});
});
});
function updatePost(id, data, callback) {
Post
.findById(_id)
.exec((err, post) => {
if(err) return callback(err);
if(!post) return callback('not found');
post = _.extend(post, data);
post.save(() => callback(null, post));
});
}
router.put('/:id', (req, res) => {
updatePost(req.params.id, req.body, (err, post) => {
if(err) return res.status(500).send({success: false, err});
res.send({success: true, post});
});
});
router.post('/:id', (req, res) => {
updatePost(req.params.id, req.body, (err, post) => {
if(err) return res.status(500).send(err);
res.redirect('/backend/posts');
});
});
function createPost(data, callback) {
let post = new Post();
post = _.extend(post, req.body);
post.save((err) => callback(err, post));
}
router.post('/', (req, res) => {
createPost(req.body, (err, post) => {
if(req.xhr) {
if(err) return res.status(500).send({success: false, err});
return res.send({success: true, post});
}
if(err) return res.status(500).send(err);
res.redirect('/backend/posts');
});
});
module.exports = router;
As I understand it, app.use('/', ...) will match all requests which is why your check method is being called for all requests.
More good info here.
From Express docs:
A route will match any path that follows its path immediately with a
“/”.
For example: app.use("/apple", ...) will match “/apple”,
“/apple/images”, “/apple/images/news”, and so on.
I would suggest refactoring to:
var check = function(req, res, next) {
if (!req.session.status && req.path != '/user/login') {
res.redirect('/user/login');
}
else {
next();
}
}
app.use(check);
app.use('/', index);
app.use('/user', users);
So check will execute for all requests but only redirect if the request session is falsey and request path doesn't match /user/login
Keep in mind your code will fail if req.session is not set...