I'm really new to Nodejs. I'm trying to write some "clean" code with it. I'm using MySQL as my database.
I would like to use MVC as my design pattern. Currently I would like to implement the "User" as CRUD. I don't like that all my code is in app.ts
I don't use some mappers or something else just "normal" SQL queries.
import express = require('express')
import bodyParser = require('body-parser')
import db = require('mysql')
import session = require('express-session')
import {Connection, MysqlError} from "mysql"
let connection: Connection = db.createConnection({
host: 'localhost',
user: 'admin',
password: 'secret',
database: 'test'
});
var app = express();
app.use('/views', express.static(__dirname + '/views'))
app.use('/public', express.static(__dirname + '/public'))
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json());
app.get('/', function (req, res) {
connection.connect(function(err) {
if (err) {
console.error('error connecting: ' + err.stack);
return;
}
console.log('connected as id ' + connection.threadId);
});
});
app.listen(8080, function () {
console.log('Listening on port 8080!');
console.log('____________________________________')
console.log('____________________________________')
console.log('____________________________________')
console.log("PLEASE VISIT ➡️ ➡️ ➡️ ➡️ http://localhost:8080/views/index.html ⬅️ ⬅️ ⬅️ ⬅️")
console.log('____________________________________')
});
Normally the connection should be in the model right? How I can use the controllers and the models? How I can use them in my app.js
?
Thank you so much for your help I really appreciate it.
Node and Express don’t come with a strict file and folder structure. Instead, you can build your web app any way you like.This also gives us freedom to follow any pattern.
Lets take a small sample project for an example and use it as an inspiration for the design you wish to achieve.We can use Mongoose/MongoDB for this.But this can be easily switched with simple sql queries in your scenario.
Folder structure would resemble something like this,
project/
controllers/
users.js
helpers/
dbHelper.js
middlewares/
auth.js
models/
user.js
routes/
users.js
tests/
app.js
package.json
Your app.js will start the server and initialize all middlewares/routes.Middlewares can take care of session/authentication.
app.js
import express from 'express';
import bodyparser from 'body-parser';
import logger from 'logger-module';
import users from './routes/users';
import dbHelper from './helpers/dbHelper';
const app = express();
const port = process.env.PORT || 3000;
dbHelper.init();
logger.info('App is starting...');
app.use(cors());
app.use(bodyparser.json());
app.use(bodyparser.urlencoded({
extended: true,
}));
app.use('/users', users);
app.use((err, req, res, next) => {
logger.error(err);
const message = err.message || 'Server Failure';
const response = {
error: {
message,
err.stack
}
};
return res.status(500).send(response);
});
app.use((req, res) => {
res.status(404).send("Sorry can't find that!");
});
app.listen(port, (err) => {
if (err) {
logger.error(err);
}
logger.info(`App listening on port ${port}`);
});
auth.js
import passportJwt from 'passport-jwt';
const JwtStrategy = passportJwt.Strategy;
const ExtractJwt = passportJwt.ExtractJwt;
import User from '../models/User';
module.exports = function(passport, jwtConfig) {
var opts = {};
opts.secretOrKey = jwtConfig.secret;
opts.issuer = jwtConfig.issuer;
opts.audience = jwtConfig.audience;
opts.jwtFromRequest = ExtractJwt.fromAuthHeader();
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findOne({
_id: jwt_payload.sub
}).lean().exec(function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
done(null, user);
} else {
done(null, false, 'User found in token but not found in records');
}
});
}));
};
The db helper will connect to db and hold the instance of the connection which you can use in your controllers.
import mongoose from 'mongoose';
function init() {
const DB_PORT = process.env.BC_DB_PORT;
const DB_HOST = process.env.IP;
const DB_NAME = process.env.BC_DB_NAME;
let url = `mongodb://${DB_HOST}:${DB_PORT}/${DB_NAME}`;
mongoose.connect(url, function(err) {
if (err) {
console.log(`Database connectivity error - ${url}`);
}
});
}
function getMongoose() {
return mongoose;
}
module.exports = {
init,
getMongoose
};
The models will hold your schema and can be consumed in Controllers.It will also hold Model specific logic like encrypting passwords.In your case you can simply save your queries here.
users.js
import mongoose from '../helpers/dbHelper.js';
import bcrypt from 'bcrypt';
var Schema = mongoose.getMongoose().Schema;
var User = new Schema({
_id: {
type: String,
required: true
},
password: {
type: String,
required: true
},
firstname: String,
lastname: String,
email: {
type: String,
trim: true,
unique: true,
required: true
},
mobile: {
type: String,
trim: true,
unique: true,
required: true
}
}, {
timestamps: true
});
User.pre('save', function(next) {
var user = this;
if (this.isModified('password') || isNew) {
bcrypt.genSalt(10, function(err, salt) {
if (err) {
return next(err);
}
bcrypt.hash(user.password, salt, function(err, hash) {
if (err) {
return next(err);
}
user.password = hash;
next();
});
});
} else {
return next();
}
});
User.pre('findOneAndUpdate', function(next) {
var user = this;
if (this._update.password) {
bcrypt.genSalt(10, function(err, salt) {
if (err) {
return next(err);
}
bcrypt.hash(user._update.password, salt, function(err, hash) {
if (err) {
return next(err);
}
user._update.password = hash;
next();
});
});
} else {
return next();
}
});
User.methods.comparePassword = function(password, cb) {
bcrypt.compare(password, this.password, function(err, isMatch) {
if (err) {
return cb(err);
}
cb(null, isMatch);
});
};
module.exports = mongoose.model('User', User);
The routes will be holding the express middlewares that act as service endpoints and will be consuming the controllers.The routers are the apt place to hold your api docs too if you plan to use something like apidoc.
routes/users.js
import {router} from "express";
import userControllerfrom '../controllers/users';
router.get("/", function(req, res, next) {
userController.findAllUsers(req, res, next);
});
router.delete("/:userId", function(req, res, next) {
userController.deleteUser(req, res, next);
});
router.get("/:userId", function(req, res, next) {
userController.findUser(req, res, next);
});
router.put("/:userId", function(req, res, next) {
userController.updateUser(req, res, next);
});
module.exports = router;
Controllers will have the logic to act on models.They also hold the validations for the model.
controllers/user.js
import HttpStatus from "http-status";
import User from '../models/User';
function deleteUser(req, res, next) {
let userId = req.params.userId;
User.remove({
_id: userId
}, function(err, {
result
}) {
//Logic to handle
});
}
function findUser(req, res, next) {
let userId = req.params.userId;
User.findOne({
_id: userId
}, {
password: 0,
__v: 0
}).lean().exec().then(function(user) {
//Logic to handle
}).catch(function(err) {
err["info"] = "Error when finding user";
next(err);
});
}
function findAllUsers(req, res, next) {
User.find(req.query, {
password: 0,
__v: 0
}).lean().exec().then(function(users) {
//Logic to handle
}).catch(function(err) {
err["info"] = "Error when finding all users";
next(err);
});
}
function updateUser(req, res, next) {
let userId = req.params.userId;
User.findOneAndUpdate({
_id: userId
}, req.user, {
new: true
}, function(err, user) {
//Logic to handle
});
}
module.exports = {
deleteUser,
findUser,
findAllUsers,
updateUser
};