I'm trying to handle all my errors in my backend in a streamlined fashion. In one of my endpoints I save some documents to my mongoose database and if there are any errors, I catch them and propagate them to my error handler by calling next(err). This works for errors outside the Promise, but not when next() is called inside the catch() clause. My endpoint is defined like this:
router.post("/", (req, res, next) => {
let listToAdd = req.body;
if (!(listToAdd instanceof Array)) {
listToAdd = [listToAdd];
}
let persons = listToAdd.map(p => {
return new Person(p)
});
Promise.all(persons.map(p => p.save()))
.then(saved => {
res.status(201).send(`Successfully saved document(s) with id(s): ${persons.map(p => p._id.toString())}`)
})
.catch(err => {
next(err); // This error never reaches my middleware
});
next(); // errors here be handled by middleware
});
My app.js, where my middleware is registered:
const express = require("express");
const cors = require("cors");
const bodyParser = require("body-parser");
const session = require('express-session');
const mongoose = require('mongoose');
const MongoStore = require('connect-mongo')(session);
// Routes
const personalFileRouter = require("./endpoints/person");
const userRouter = require("./endpoints/user");
// Custom Middleware
const {myRequestLoggingMiddleware} = require("./middleware/express_logging");
const {myErrorLoggingMiddleware} = require("./middleware/express_logging");
// Create a new express app
const app = express();
// Use CORS to allow communication to frontend
app.use(cors());
// use bodyparser to parse url body
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({extended: true}));
// Use Express Sessions to track user logins
app.use(session({
secret: process.env.PRIVATE_KEY,
resave: true,
saveUninitialized: false,
store: new MongoStore({
mongooseConnection: mongoose.connection
})
}));
// Whenever we get a request on the form "/whatever" it should use the routes file to redirect
app.use("/person/", personalFileRouter);
app.use("/user/", userRouter);
app.use(myErrorLoggingMiddleware); // <-- Register my middleware last
module.exports = app;
And finally, my middleware error handler
function myErrorLoggingMiddleware(err, req, res, next) { // Never reached :(
if (res.headersSent) {
return next(err)
}
if (err instanceof mongoose.Error.ValidationError) {
validationErrorHandler(err, req, res);
} else {
defaultErrorHandler(err, req, res);
}
next();
}
exports.myErrorLoggingMiddleware = myErrorLoggingMiddleware;
Well, this is embarrassing. I'm not a 100% sure why, but I reckon that when I call next() below my promise, which is executed before the promise is resolved, the function has run its course and next is "used up". So when the promise later resolves, next() is an empty function that does not reference any middleware. My solution is then to simply remove the last next() and only call next inside my promise.
Related
I am working on implementing custom error handling in a MongoDB MERN application. I’m also using Mongoose and passport-local-mongoose.
When a user registers for a new account, they should get a specific error message in the chrome console saying, ‘username already exists’. I know it is a bad idea to show all the server error messages to the user, but I would like to show a select few.
Links that I have tried:
https://github.com/saintedlama/passport-local-mongoose
Passport-local-mongoose : Authenticate user right after registration
https://www.geeksforgeeks.org/nodejs-authentication-using-passportjs-and-passport-local-mongoose/
Here is the code:
server/controller/auth.js
const { User } = require('../models');
const register = async function (req, res) {
try {
const user = new User({ username: req.body.username });
await user.setPassword(req.body.password);
await user.save();
}
catch (err) {
console.log(`error inside save ${err}`);
res.status(500).send(err);
}
};
const login = async function (req, res) {
//code block under construction
console.log(`login!!!!!`);
};
const logout = function (req, res) {
req.session.destroy();
res.end();
};
exports.login = login;
exports.register = register;
exports.logout = logout;
server/models/User.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const passportLocalMongoose = require('passport-local-mongoose');
const userSchema = new Schema({
username: { type: String, unique: true },
password: { type: String}
});
userSchema.plugin(passportLocalMongoose);
const User = mongoose.model('User', userSchema);
module.exports = User;
server/routes/api/auth/index.js
const router = require('express').Router();
const passport = require('../../../config/passport');
const authController = require('../../../controllers/auth');
router.route('/logout').get(authController.logout);
router.route('/register').post(authController.register);
router.use(passport.authenticate('local', {
session: true
}));
// Matches with '/api/auth'
router.route('/login').post(authController.login);
module.exports = router;
server/server.js
const path = require('path');
const express = require('express');
const passport = require('./config/passport');
const mongoose = require('mongoose');
const cors = require('cors');
const session = require('express-session');
const helmet = require('helmet');
const morgan = require('morgan');
const corsOptions = require('./config/cors.js');
const routes = require('./routes');
const { v1: uuidv1 } = require('uuid');
// console.log(uuidv1());
const PORT = process.env.PORT || 3001;
const app = express();
mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost/puzzlegallery', {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
useFindAndModify: false
});
mongoose.set("useCreateIndex", true);
// Define middleware here
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(helmet({ contentSecurityPolicy: false }));
app.use(session({ secret: 'sassy', resave: false, saveUninitialized: true }));
app.use(passport.initialize());
app.use(passport.session());
app.use(cors(corsOptions));
app.use(morgan('dev'));
app.use(routes);
// for Reactjs ##################
// Serve up static assets (usually on heroku)
if (process.env.NODE_ENV === 'production') {
app.use(express.static('client/build'));
}
// #################################################
if (process.env.NODE_ENV === 'production') {
app.get('*', (_, res) => {
res.sendFile(path.join(__dirname, '../client/build/index.html'));
});
}
app.listen(PORT, (err) => {
if (err) throw err;
console.log(
`🌎 Server is Ready and Listening on http://localhost:${PORT}`
); // eslint-disable-line no-console
});
Login page:
Server console:
Here is a link to the repo also for more context: https://github.com/BenjDG/puzzle-gallery
Thanks for any help you can offer!!!
If you want the caller to handle errors gracefully, you might consider returning a 200 response, like:
{
"success": true
}
or...
{
"success": false,
"errorCode": "UsernameAlreadyExists",
"message": "This username already exists."
}
It will be the responsibility of the caller to check the success field to make sure the request succeeded. If you want, you can display the friendly message directly in your app, or you can use an "error code" to determine what to display. This is helpful for an API shared across multiple apps, and you want to display different messages, or if you support a multi-lingual UI, and want to translate the message.
Quick aside:
I know it is a bad idea to show all the server error messages to the user, but I would like to show a select few.
I'm sure you've seen articles that warn against this, so just a bit of clarification. You don't want to pass internal error messages and stack traces to your callers, as this exposes more information about your system than most clients should know. Attackers might use this information to learn more about your implementation, and use that to exploit your system.
In general, there is little harm in returning a friendly error message, or a sub-status code, esp for 4xx errors, to help the caller understand how they need to re-submit the request to get a successful response. The important thing is to abstract away all underlying implementation details, so don't just pass an error message directly from Mongoose to your caller, catch the exception, and send an appropriate response.
Because you're dealing with authentication - you also need to be careful about exposing too much to your caller. For example - exposing a "Check Username" endpoint will make it easy for someone to brute force your API to get a handful of valid users in your app.
When you send a status code that is not in the range 200, it is considered as an "exception" in the client code. For Axios specifically, the catch block is executed. In your code, it is
.catch((err) => {
console.error(err);
});
So you have the error message in the console.
Recommend: You can use the status 200. And the better is to check if the email is already in the database before adding a new user.
I think the error 500 is used in case you aren't aware of the error. In this case, we can handle the duplication error by checking before.
For router.use, it does not work like this anymore:
router.use("/api", apiRoutes);
Instead an error is thrown:
throw new typeerror('router.use() requires a middleware function but got a ' + gettype(fn))
How do I re-purpose that expression so that it works? I have not found any examples that were useful so far. Here is some of my sample code:
routes/index.js (this does not work)
const path = require("path");
const router = require("express").Router();
const apiRoutes = require("./api");
// API Routes
router.use("/api", apiRoutes);**// this throws an error**
router.use(function(req, res) {
res.sendFile(path.join(__dirname, "../client/build/index.html"));
});
module.exports = router;
Here is an example of my attempt to re-purpose but I do not think it's correct:
var path = require("path");
var router = require("express").Router();
var apiRoutes = require("./api");
//API Routes
//authRouter.use(require('./authenticate').basic(usersdb))
//router.use("./api", apiRoutes);
console.log("Hitting API routes...")
router.use("./api", function(req, res, next) { **//re-purpsose attempt here**
res.send(apiRoutes)
console.log("API Routes:", apiRoutes)
next()
});
console.log("API Routes hit")
// //If no API routes are hit, send the React app
// router.use(function(req, res) {
// res.sendFile(path.join(__dirname, "../client/public/index.html"));
// });
module.exports = router
This is the overall error I'm getting (404 returned):
GET /api/website_1_function_call/scrape 404 4.004 ms - 173
I know that this may be due to something else indirectly but I really am not sure about the router.use part.
I know for sure that the routes are not being hit properly and would like to fix.
Any advice would be appreciated. Thank you in advance.
Here is more code:
server.js
require("dotenv").config();
var express = require("express");
var cors = require('cors');
var bodyParser = require('body-parser');
var logger = require("morgan");
//const mongoose = require("mongoose");
var db = require("./models")
var routes = require("./routes");
var app = express();
var PORT = process.env.PORT || 3001;
var path = require('path');
//Define middleware here
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(bodyParser.json());
//Serve up static assets (usually on heroku)
if (process.env.NODE_ENV === 'production') {
app.use(express.static("client/build"));
}
app.use(cors());
app.use(logger("dev"));
//Add routes, both API and view
app.use(routes);
//replaced with below:
//app.use(app.router);
//routes.initialize(app);
// //Connect to the Mongo DB
// mongoose.connect(process.env.MONGODB_URI || "mongodb://localhost/kaibru");
var syncOptions = { force: false };
// If running a test, set syncOptions.force to true
// clearing the `testdb`
if (process.env.NODE_ENV === "test") {
syncOptions.force = true;
};
// Starting the server, syncing our models ------------------------------------/
db.sequelize.sync(syncOptions).then(function() {
app.listen(PORT, function() {
console.log(
"==> 🌎 Listening on port %s. Visit http://localhost:%s/ in your browser.",
PORT,
PORT
);
});
});
// //Start the API server
// app.listen(PORT, function() {
// console.log(`🌎 ==> API Server now listening on PORT ${PORT}!`);
// });
routes/index.js
var path = require("path");
var router = require("express").Router();
var apiRoutes = require("./api");
//API Routes
//authRouter.use(require('./authenticate').basic(usersdb))
//router.use("/api", apiRoutes);
console.log("Hitting API routes...")
router.use("/api", function(req, res, next) { // this is my re-purpose
attempt
apiRoutes
console.log("API Routes:", apiRoutes)
// next()
}); // this is my r-purpose attempt
console.log("API Routes hit")
// //If no API routes are hit, send the React app
// router.use(function(req, res) {
// res.sendFile(path.join(__dirname, "../client/public/index.html"));
// });
module.exports = router
routes/api/index.js
var router = require("express").Router();
require("./website_1");
var website_1Routes = require("./website_1_function_call");
//const userRoutes = require("./user");
//Website_1 routes
//http://localhost:3000/api/website_1_function_call/scrape
//authRouter.use(require('./authenticate').basic(usersdb))
//router.use("/website_1_function_call", website_1Routes);
//experimental use
router.use("/website_1_function_call", function(req, res, next) { // this is my re-purpose attempt
website_1Routes
console.log("website_1Routes:", website_1Routes)
// next()
}); //this is my re-purpose attempt
//router.use("/user", userRoutes);
module.exports = router
routes/api/website_1_function_call.js
require("./website_1");
require("./website_1_db");
require("./website_1_router");
//Call scrape functions from website_1 file
mainscrape();
//specificScrape() //let's leave this one dormant for now
//Now for saving to database
saveToDatabase();
//Now for the routes
routing();
I think my re-purpose attempt worked ( I removed next() since there are no defined routes right after). It seems to be processing. However, now my response hangs and this happens:
GET /api/website_1_function_call/scrape - - ms - -
This prints in the browser console:
GET http://localhost:3000/api/website_1_function_call/scrape
net::ERR_EMPTY_RESPONSE
0.chunk.js:871 Uncaught (in promise) Error: Network Error
at createError (0.chunk.js:871)
at XMLHttpRequest.handleError (0.chunk.js:366)
So now I think my scraper code and my code to update the database does not work.
Scrape function code:
//var express = require("express");
var router = require("express").Router();
require("../../controllers/website_1controller");
//requiring this website's models
var Items_1 = require("../../models/website_1");
//require("./website_1_db");
//require("./website_1_router");
// Our scraping tools
// Axios is a promised-based http library, similar to jQuery's Ajax method
// It works on the client and on the server
var axios = require("axios");
var cheerio = require("cheerio");
mainscrape = function() {
//Now to configure the routes
router.get("/scrape", function(req, res) {
//instead of simple res.render, user router.get
console.log("scraping started...");
//Grab the html body with axios
axios.get("url placeholder").then(function(response) {
//Load to cheerio and save to $ selector
console.log("Scraping all greenheartshop mainpage...");
var $ = cheerio.load(response.data);
var output = [];
var promises = [];
//Now we need to grab the title reference for each article
$("article").each(function(i, element) {
//save empty result object
var result = {};
//thumbnail
result.thumbnail = $(this)
//.children("article.product-grid-item.product-block").html()
.children("figure.product-item-thumbnail")
.children("a")
.attr("href")
//console.log("result thumbnail")
//console.log(result)
console.log(result.thumbnail)
var result = {}
//details
result.detail= $(this)
//.children("product-item-mask").html()
.children("div.product-item-details")
// .children("div.product-item-brand")
// .children("h5.product-item-title")
// .children("a")
// .children("div.product-item-price")
//.children("product-price-line")
//.children("price-value")
.text()
//result.detail = result.detail.trim();
//console.log("result detail")
//console.log(result)
console.log(result.detail)
//Capture the scraped data and save to database
console.log("Capturing Scrape")
if(result.detail !== '') {
var promise = Items_1
.saveToDatabase(result, result, {upsert:true, new:true})
console.log("saveToDatabase");
promises.push(promise);
}
Promise.all(promises).then((data) => {
res.json(data);
});
//saveToDatabase();
// if (result.thumbnail !== {} && result.detail !== "") {
// var promise = Items_1
// // .items_1_create({
// // resultThumbnail: result.thumbnail,
// // resultDetails: result.detail
// // })
// promises.push(promise)
// // .then(dbModel => output.push(dbModel));
// Promise.all(promises).then((data) => {
// res.json(data)
// })
// }
});
});
//Now to CREATE the results using controller file
// console.log("creating items in the database now...")
// router.post('/scrape', website_1Controller.items_1_create);
//Now to display the results
// console.log("Items now being displayed...")
// router.get('/scrape/display', website_1Controller.items_1_list)
});
}
module.exports = router;
module.exports = mainscrape;
module.exports = specificScrape;
Code to update the database:
require("../../controllers/website_1controller");
require("./website_1");
var Items_1 = require( "../../models");
//After scraping the main page, the following function is to save to the
database
saveToDatabase = function() {
//prepare the data
var result = {}
var dataToStore = Items_1.items_1_create
console.log(dataToStore)
//console.log(items_1_create)
//insert data to the database
// dataToStore.save().// We will not sue this part for now
// then(() => {
// console.log("Data successfully saved");
// }).catch(err => {
// console.log("Error: ", err);
// });
}
module.exports = saveToDatabase;
Code for final routing (after scrape is complete)
var website_1Controller = require("../../controllers/website_1controller");
var router = require("express").Router();
routing = function() {
//Now to CREATE the results using controller file
console.log("creating items in the database now...")
//router.route("/browse")
router.post('/browse', website_1Controller.items_1_create);
router.get('/browse', website_1Controller.items_1_list);
//Now to display the results
console.log("Items now being displayed...")
//router.route("/browse:search")
router.get('/:search', website_1Controller.items_1_specific);
};
require("./website_1");
module.exports = routing;
module.exports = router;
models
'use strict';
// Dependencies
// =============================================================
// Sequelize (capital) references the standard library
//var Sequelize = require("sequelize");
// sequelize (lowercase) references our connection to the DB.
//var sequelize = require("../config/connection.js");
// Creates a "Items_1" model that matches up with DB
module.exports = function(sequelize, DataTypes) {
var Items_1 = sequelize.define("Items_1", {
// the routeName gets saved as a string
detail: DataTypes.STRING,
// the name of the character (a string)
thumbnail: DataTypes.BLOB,
// the character's role (a string)
//role: Sequelize.STRING,
// the character's age (a string)
//age: Sequelize.INTEGER,
// and the character's force points (an int)
//forcePoints: Sequelize.INTEGER
}, {
// disable the modification of tablenames; By default, sequelize will
automatically
// transform all passed model names (first parameter of define) into
plural.
// if you don't want that, set the following
freezeTableName: true
});
return Items_1;
//Syncs with DB
//Items_1.sync();
// Makes the Items_1 Model available for other files (will also create a table)
};
controller
// *********************************************************************************
// website_1controllers.js - this file offers a set of routes for displaying and saving data to the db
// *********************************************************************************
// Dependencies
// =============================================================
var db = require("../models");
//display results for mainpage scrape
exports.items_1_create = function(req, res) {
db.Items_1.findOneAndUpdate(req.body, req.body, {upsert: true, new:
true})
.then(dbModel => res.json(dbModel))
.catch(err => res.status(422).json(err))
console.log("findOneAndUpdate complete")
},
exports.items_1_list = function(req,res) {
db.Items_1.findAll({})
},
exports.items_1_specific = function(req,res) {
db.Items_1.findById(req.params.search)
},
function(err, results) {
if (err) { return next(err); } //Error in API usage.
if (results.result.thumbnail==={} && results.result.detail==="") {//No
Results.
var err = new Error('Results not found');
err.status = 404;
return next(err)
}
//Successful, so render
res.render("click_results", { title: 'Click Results', resultThumbnail:
result.thumbnail, resultDetails: result.detail });
}
So the new issue is that the response hangs. I think it's because the code to update the database does not work (using sequelize). Let me know if anything else is needed and thank you in advance.
Thanks for all of the input everyone. After reviewing I found out that the function itself does not have to be re-purposed as I initially thought... I didn't know that if, for example, you are using "router.use("/directoy", directory) and you are using it in succession to point to different directories, the final directory hit must have a defined route like router.get(). I modularized my code to the point where the final directory was just a list of functions (one of these functions had the router.get method). This did not work. When I point the file directly to the code containing the router.get method, my scraped data returns to the terminal. Just thought I'd share my findings at least because I didn't know this at all . Many thanks to #mehta-rohan and #Anand Undavia for the insights. I'm still trying to get the data to render to the page but that's a different problem altogether.
EDIT: After a deal of working towards a solution I am convinced this has to do with the way the package.json file compiles a lot of the site on the fly currently. Webpack, and babble are involved. I think the solution will setting up a test server that works with a fully compiled site.
I am working my way through a node course, and I want to stop before I go any further and add testing to it.
ATM I'd just like to be able to test the home route kicks back a 200. With postman it does no problem, but I can't get mocha to test it.
app.js:
const express = require("express");
const session = require("express-session");
const mongoose = require("mongoose");
const MongoStore = require("connect-mongo")(session);
const path = require("path");
const cookieParser = require("cookie-parser");
const bodyParser = require("body-parser");
const passport = require("passport");
const { promisify } = require("es6-promisify");
const flash = require("connect-flash");
const expressValidator = require("express-validator");
const routes = require("./routes/index");
const helpers = require("./helpers");
const errorHandlers = require("./handlers/errorHandlers");
// create our Express app
const app = express();
// view engine setup
app.set("views", path.join(__dirname, "views")); // this is the folder where we keep our pug files
app.set("view engine", "pug"); // we use the engine pug, mustache or EJS work great too
// serves up static files from the public folder. Anything in public/ will just be served up as the file it is
app.use(express.static(path.join(__dirname, "public")));
// Takes the raw requests and turns them into usable properties on req.body
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// Exposes a bunch of methods for validating data. Used heavily on userController.validateRegister
app.use(expressValidator());
// populates req.cookies with any cookies that came along with the request
app.use(cookieParser());
// Sessions allow us to store data on visitors from request to request
// This keeps users logged in and allows us to send flash messages
app.use(
session({
secret: process.env.SECRET,
key: process.env.KEY,
resave: false,
saveUninitialized: false,
store: new MongoStore({ mongooseConnection: mongoose.connection })
})
);
// // Passport JS is what we use to handle our logins
app.use(passport.initialize());
app.use(passport.session());
// // The flash middleware let's us use req.flash('error', 'Shit!'), which will then pass that message to the next page the user requests
app.use(flash());
// pass variables to our templates + all requests
app.use((req, res, next) => {
res.locals.h = helpers;
res.locals.flashes = req.flash();
res.locals.user = req.user || null;
res.locals.currentPath = req.path;
next();
});
// promisify some callback based APIs
app.use((req, res, next) => {
req.login = promisify(req.login, req);
next();
});
// After allllll that above middleware, we finally handle our own routes!
app.use("/", routes);
// If that above routes didnt work, we 404 them and forward to error handler
app.use(errorHandlers.notFound);
// One of our error handlers will see if these errors are just validation errors
app.use(errorHandlers.flashValidationErrors);
// Otherwise this was a really bad error we didn't expect! Shoot eh
if (app.get("env") === "development") {
/* Development Error Handler - Prints stack trace */
app.use(errorHandlers.developmentErrors);
}
// production error handler
app.use(errorHandlers.productionErrors);
// done! we export it so we can start the site in start.js
module.exports = app;
The application is set up to run routes through a file at routes/ called index.js. That file then calls up the view file...
My test can't seem to get properly routed though.
const expect = require("expect");
const request = require("supertest");
const app = require("./../../app");
describe("Dummy Test", () => {
it("Should return 5", () => {
const result = 2 + 3;
expect(5);
});
});
describe("Get /home", () => {
it("should get home", done => {
request(app)
.get("/home")
.expect(200)
.end(done);
});
});
It always returns a 500. I can make the repo public if a deeper look might help.
Not sure if I'll solve it, but hoping this at least sparks some new debugging ideas for you. I normally use superagent with Jest to test, but this looks like a more or less similar setup.
I did some code comparison to the docs (https://www.npmjs.com/package/supertest).
In this example, there is some error handling on the .end(). Wondering if adding that might help you diagnose?
describe('POST /users', function() {
it('responds with json', function(done) {
request(app)
.post('/users')
.send({name: 'john'})
.set('Accept', 'application/json')
.expect(200)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
});
Also, this example shows the done being added as a comma separated instead of a .end(done) on it's own line. Your current way is also shown, but it's just another way to try.
describe('GET /user', function() {
it('respond with json', function(done) {
request(app)
.get('/user')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200, done);
});
});
If none of that helps, my last thoughts are what is your '/home' route actually returning? I see the routes import in your app file, but cannot see that actual routes for reference. Have you tried additional console.log's / error handling in your /home route to examine the back-end's perspective on what is being sent?
I would like to protect some endpoints in my express app, I want to create something simple to manage if my app became a big app...now I'm doing something like this:
setProtected(router) {
const self = this;
router.use(this.auth);
...
}
setPublic(router) {
const self = this;
...
}
getRouter() {
const router = express.Router();
this.setPublic(router);
this.setProtected(router);
return router;
}
with:
auth(req, res, next) {
if(req.isAuthenticated()) {
console.log('req.isAuthenticated()', req.isAuthenticated());
return next();
}
return res.send(401);
}
the problem in this case is that is difficult maintain and it doesn't work well as if I have /:id in my publicRoute and for example /my-items in my protected route when I'm not logged and I try to reach /my-items I get the code of /:id.
Another idea was to create a json with the list of all my urls with same information like protected/not protected and eventual roles and then change auth with something like:
import urls from './urls';
auth(req, res, next) {
if (urls[req.url] == 'public') {
return next()
}
else if (urls[req.url] == 'protected' && req.isAuthenticated()) {
return next();
}
return res.send(401);
}
whats the best way for you?
You can chain middlewares:
eg.
const authenticate = (req, res, next) {
.. some auth logic
next();
}
app.use('/', main...
app.use('/profile', authenticate, otherMiddleware,
app.use('/admin', authenticate, isAdmin, otherMiddleware...
in your main file (server.js) import the routes and use the middleware there :)
server.js
const express = require('express')
const cors = require('cors')
const app = express()
// import admin routes
const adminRoute = require('./app/routes/admin.route.js')
// Add middleware for parsing URL encoded bodies (which are usually sent by browser)
app.use(cors())
// Add middleware for parsing JSON and urlencoded data and populating `req.body`
app.use(express.urlencoded({ extended: false }))
app.use(express.json())
// homepage route
app.get("/", (req, res) => {
res.json({ message: "Hello World" })
})
// restricted by middleware "isAdmin"
app.use('/api/v1', isAdmin, adminRoute)
app.listen(8008).on('listening', () => {
console.log('Server is running on 8008')
})
admin.route.js
const express = require('express')
const admin = require('../controllers/admin.controller.js')
const router = express.Router()
// get all admin users
router.get('/users', (req, res, next) => {
admin.getAdminUsers(req, res, next)
})
module.exports = router
I am using express backend with a react frontend everything is working fine but occasionally i get error
Cant set header after they are sent
and server gets down.i searched few ways this error might happen but in my code i could not find such cases.i tried to be simple as possible in the code.can anyone please point me what might be the issue?
Server.js file
// call the packages we need
const addItem = require('./controllers/addItem');
const addCategory = require('./controllers/addCategory');
const addSubCategory = require('./controllers/addSubCategory');
const getSubCategory = require('./controllers/getSubCategoryByCategory');
const getCategory = require('./controllers/getAllCategory');
const getAllItems = require('./controllers/getAllItems');
const cors = require('cors');
const express = require('express');
// call express
const app = express(); // define our app using express
const bodyParser = require('body-parser');
// configure app to use bodyParser()
// this will let us get the data from a POST
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cors());
const port = process.env.PORT || 8080; // set our port
// ROUTES FOR OUR API
// =============================================================================
const addItemRoute = express.Router(); // get an instance of the express Router
const getCategoryRoute = express.Router();
const addCategoryRoute = express.Router();
const addSubCategoryRoute = express.Router();
const getSubCategoryRoute = express.Router();
const getAllItemsRoute = express.Router();
getCategoryRoute.get('/get_category', (req, res) => {
getCategory(res);
});
addCategoryRoute.post('/add_category', (req, res) => {
addCategory(req.body.name, res);
});
getSubCategoryRoute.get('/get_subcategory/:catId', (req, res) => {
getSubCategory(req.params.catId, res);
});
addSubCategoryRoute.post('/add_subcategory', (req, res) => {
addSubCategory(req.body.name, req.body.cat_id, res);
});
// code, name, quantity, length, description and subcategory id should be passed as parameters
addItemRoute.post('/add_item', (req, res) => {
addItem(req.body.item, res);
});
getAllItemsRoute.get('/get_items', (req, res) => {
getAllItems(res);
});
// more routes for our API will happen here
// REGISTER OUR ROUTES -------------------------------
// all of our routes will be prefixed with /api
app.use('/api', addItemRoute);
app.use('/api', getCategoryRoute);
app.use('/api', addCategoryRoute);
app.use('/api', addSubCategoryRoute);
app.use('/api', getSubCategoryRoute);
app.use('/api', getAllItemsRoute);
// START THE SERVER
// =============================================================================
app.listen(port);
console.log(`Server started on port ${port}`);
getAllCategories() function
Object.defineProperty(exports, '__esModule', {
value: true,
});
const pool = require('./connection');
module.exports = function (res) {
pool.getConnection((err, connection) => {
if (err) {
connection.release();
return res.json({ code: 100, status: 'Error in connection database' });
}
console.log(`connected as id ${connection.threadId}`);
connection.query('select * from category;', (err, rows) => {
connection.release();
if (!err) {
return res.json(rows);
}
});
connection.on('error', err => res.json({ code: 100, status: 'Error in connection database' }));
});
};
If you get an error in connection.query() you send a response with res.json(). This error is caught in connection.on('error') where you send another response. You can't send two responses to the same request. It seems that in this case, you don't really need connection.on() at all or if you have it to catch other errors, don't send a response on connection.query()'s error.