I'm taking a course on NodeJS, there were a few assignments related to routing, everything works fine except this part which seems a little odd: For some reason, I cannot read the parameter ID being passed to the mounted router.
dish.js
const express = require('express');
const bodyParser = require('body-parser');
const dishRouter = express.Router();
dishRouter.use(bodyParser.json());
dishRouter.route('/')
.all((req,res,next) => {
res.statusCode = 200;
res.setHeader('Content-Type','text/plain');
next();
})
.get((req,res) => {
console.info('Info: ',req);
res.end(`Sending details of the dish back to you: ${req.params.dishId}`);
})
.post((req,res) => {
res.statusCode = 403;
res.end(`Operation not supported: ${req.params.dishId}`);
})
.put((req,res) => {
res.write(`Updating the dish...: ${req.params.dishId} \n` );
res.end(`Will update this dish: ${req.body.name} with details: ${req.body.description}`);
})
.delete((req,res) => {
res.end(`Deleting this dish: ${req.params.dishId}`);
});
exports.dish = dishRouter;
dishes.js
const express = require('express');
const bodyParser = require('body-parser');
const dishesRouter = express.Router();
dishesRouter.use(bodyParser.json());
dishesRouter.route('/')
.all((req,res,next) => {
res.statusCode = 200;
res.setHeader('Content-Type','text/plain');
next();
})
.get((req,res) => {
res.end('Sending all dishes back to you');
})
.post((req,res) => {
res.end(`Will add the dish: ${req.body.name} with details: ${req.body.description}`);
})
.put((req,res) => {
res.statusCode = 403;
res.end(`Operation not supported.`);
})
.delete((req,res) => {
res.end(`Deleting all dishes.....`);
});
exports.dishes = dishesRouter;
index.js
const express = require('express');
const morgan = require('morgan');
const bodyParser = require('body-parser');
const http = require('http');
const dishRouter = require('./routes/dish');
const dishesRouter = require('./routes/dishes');
const hostname = 'localhost';
const port = 3000;
const app = express();
app.use(morgan('dev'));
app.use(bodyParser.json());
app.use('/dishes',dishesRouter.dishes);
app.use('/dishes/:dishId',dishRouter.dish);
app.use(express.static(__dirname+'/public'));
app.use((req,res,next) => {
res.statusCode = 200;
res.setHeader('Content-Type','text/html');
res.end('<html><body><h1>This is an Express Server</h1></body></html>');
});
const server = http.createServer(app);
server.listen(port,hostname,(req,res) => {
console.info(`Server running on port: ${port}, at: ${hostname}`);
})
This GET localhost:3000/dishes/123 is calling the right route, but the parameter dishId comes back as "undefined". Again, just learning nodeJS, seems like my receiver/mounted route should receive those parameters just fine, the body can be read properly, but not the params. ... thanks.
Yeah the params don't flow between routers. You're on a new router, hence new route params object.
You can check out the code for this:
https://github.com/expressjs/express/blob/master/lib/router/index.js#L43
Check out line 43 and line 53 where route.params is set to an empty object.
Some examples:
index.js
app.use('/dishes/:dishId',(req, res) => {
console.log('now I get my dishId', req.params.dishId)
});
dish.js (version 1)
dishRouter.route('/')
.get((req, res) => {
console.log('now i get nothing', req.params)
})
dish.js (version 2)
dishRouter.route('/:anotherId')
.get((req, res) => {
console.log('now we get another parameter', req.params.anotherId)
})
// the path would be /dish/123/456
I'm not sure if there is a offical-expressjs-way to pass the params object between routers.
One solution would be to create a custom handler
index.js
app.use('/dishes/:dishId', handler)
handler.js
function handler (req, res, next) {
if (req.method === 'GET') {
console.log('now we get it', req.params)
}
}
module.exports = handler
Anoter way would be to add the dishId to the request object before calling the router:
index.js
app.use('/dishes/:dishId', (req, res, next) => {
req.dishId = req.params.dishId
router(req, res, next)
})
dish.js
const express = require('express')
const router = express.Router()
router.route('/')
.get((req, res) => {
console.log('nothing here', req.params)
console.log('dishId', req.dishId)
})
module.exports = router
Third way would be to send the params as options to a router function
index.js
app.use('/dishes/:dishId', (req, res, next) => {
router(req.params)(req, res, next)
})
dish.js
function createRouter (options) {
const router = express.Router()
router.route('/')
.get((req, res) => {
console.log('nothing here', req.params)
console.log('but alot here', options)
})
return router
}
module.exports = createRouter
If you want you could also just put the :dishId on the router as an optional parameter
index.js
app.use('/dishes', dishesRouter)
dishes.js
const express = require('express')
const router = express.Router()
router.route('/:dishId?')
.get((req, res) => {
if (req.params.dishId) {
res.end(`Sending details of the dish back to you: ${req.params.dishId}`)
} else {
res.end('Sending all dishes back to you');
}
})
module.exports = router
Related
I'm a new learner express.js I want to test simple post and get operations with tdd mechanism. I created the test, route, index and db files but when I try to test POST method it gives me this error.
This is my routes/task.js
const express = require('express');
const router = express.Router();
router.post("/api/task", async (req,res) => {
try {
const task = await new Task(req.body).save();
res.send(task);
} catch (error) {
res.send(error);
}
})
This is my test/task.js
let chai = require("chai");
const chaiHttp = require("chai-http");
const { send } = require("process");
let server = require("../index");
//Assertion Style
chai.should();
chai.use(chaiHttp);
describe('Tasks API', () => {
/**
* Test the POST Route
*/
describe('POST /api/task', () => {
it("It should POST a new task", () => {
const task = {task: "Wake Up"};
chai.request(server)
.post("/api/task")
.send(task)
.end((err, response) => {
response.should.have.status(201);
response.body.should.be.a('string');
response.body.should.have.property('id');
response.body.should.have.property('task');
response.body.should.have.property('task').eq("Wake Up");
response.body.length.should.be.eq(1);
done();
});
});
});
});
This is my db.js
var sqlite3 = require('sqlite3').verbose()
const DBSOURCE = "db.sqlite"
let db = new sqlite3.Database(DBSOURCE, (err) => {
if (err) {
// Cannot open database
console.error(err.message)
throw err
}else{
console.log('Connected to the SQLite database.')
db.run(`CREATE TABLE IF NOT EXISTS todo (
id INTEGER PRIMARY KEY AUTOINCREMENT,
task text
)`,
(err) => {
if (err) {
// Table already created
console.log(err);
}
});
}
});
module.exports = db
And this is my index.js
const connection = require('./db');
const express = require('express');
const app = express();
const cors = require("cors");
const port = process.env.PORT || 8080;
app.use(express.json());
app.use(cors());
app.get('/', (req, res) => {
res.send('Hello World');
});
app.post('/api/task', (req, res) => {
res.status(201).send(req);
});
app.listen(port, () => console.log(`Listening on port ${port}...`));
module.exports = app;
The thing that I try to do is building a test case to test the post method. I think I couldn't built the correct relations the files.
Currently, just by doing a POST request to /api/task, the error will appear. That is because of these lines in index.js:
app.post('/api/task', (req, res) => {
res.status(201).send(req);
});
The req parameter is circular, hence cannot be JSON-stringified.
Solution
In routes/task.js export the router:
const express = require('express');
const router = express.Router();
router.post("/api/task", async (req,res) => {
try {
const task = await new Task(req.body).save();
res.send(task);
} catch (error) {
res.send(error);
}
})
// By adding this line you can export the router
module.exports = router
In index.js, include the routes/task.js file and pass it to app.use(...), also remove the now-obsolete /api/task route:
const connection = require('./db');
const express = require('express');
const app = express();
const cors = require("cors");
const taskRoutes = require("./routes/task")
const port = process.env.PORT || 8080;
app.use(express.json());
app.use(cors());
app.get('/', (req, res) => {
res.send('Hello World');
});
app.use(taskRoutes)
app.listen(port, () => console.log(`Listening on port ${port}...`));
module.exports = app;
This way we got rid of the circular structure stringifying and the tests should now pass.
I am learning a MERN stack course on Udemy and currently I am trying to retrieve the user's data from the server but I can't. I am able to retrieve the post data but the connections times out for users data. Can you guys help me find out what went wrong? Thank you in advance!
userController snippet:
exports.allUsers = (req, res) => {
const users = User.find({})
.then((users) => {
console.log(users);
})
.catch(err => console.log(err));
};
User routes snippet
const express = require('express'),
router = express.Router(),
{userById, allUsers } = require('../controllers/userController');
router.get('/users', allUsers);
router.param('userID', userById)
module.exports = router;
app.js code snippet
const express = require('express'),
app = express(),
postRoutes = require('./routes/post'),
authRoutes = require('./routes/auth'),
morgan = require("morgan"),
mongoose = require("mongoose"),
bodyParser = require("body-parser"),
cookieParser = require('cookie-parser'),
userRoutes = require('./routes/user'),
expressValidator = require('express-validator');
require('dotenv').config();
mongoose.connect(process.env.MONGO_URI,
{ useUnifiedTopology: true, useNewUrlParser: true })
.then(() => console.log("DB connected"));
app.use(morgan("dev"));
app.use(bodyParser.json());
app.use(cookieParser());
app.use(expressValidator());
app.use('/', postRoutes);
app.use('/', authRoutes);
app.use('/', userRoutes);
app.use(function (err, req, res, next) {
if (err.name === 'UnauthorizedError') {
res.status(401).json({error: "Unauthorised"});
}
});
app.listen(process.env.PORT || 3000, () => {
console.log(`SERVER AT PORT: 3000`);
});
Postman gets stuck here:
You have to end the request / respond to the request . In your userController you are missing ending/responding the request. You are just logging the user result .
Try this :
exports.allUsers = (req, res) => {
const users = User.find({})
.then((users) => {
console.log(users);
res.status(200).json(users);
})
.catch((err) => {
console.log(err);
res.status(500).json(err.message);
});
}
I´m writing some tests with chai and mocha and i am having some troubles.
For example, in the route that i paste down here, the LOGOUT calls the isLoggedIn middleware that checks if a user exists in the session.
For example, if a do this:
it('Logout', function(done) {
chai.request(baseURL)
.post('/logout')
.end(function(err, res) {
expect(err).to.be.null;
expect(res).to.have.status(204);
done();
});
});
the test faills cause i get a 401 status code. I am new on this test stuffs. I understand that i have to use sinon to get mi test pass, but i can´t get the solution.
This is my route:
'use strict';
const express = require('express');
const createError = require('http-errors');
const router = express.Router();
const bcrypt = require('bcrypt');
const User = require('../models/User');
const {isLoggedIn} = require('../helpers/middlewares');
router.post('/logout', isLoggedIn(), (req, res, next) => {
req.session.destroy();
return res.status(204).send();
});
This is the Middleware:
'use strict';
const createError = require('http-errors');
exports.isLoggedIn = () => (req, res, next) => {
if (req.session.user) {
next();
} else {
next(createError(401));
};
};
Thank you very much!!!
In your flow problem in that express middleware initialized during run express application and after becomes unavailable for stubbing. My solution is that would init stub before run express application.
test.spec.js:
const chai = require("chai"),
sinon = require("sinon"),
chaiHttp = require("chai-http"),
initServer = require("./initTestServer"),
isLoggedInMiddleware = require("./middleware");
chai.use(chaiHttp);
const { expect } = chai;
describe("Resource: /", function() {
before(function() {
sinon.stub(isLoggedInMiddleware, "isLoggedIn").callsFake(function() {
return (req, res, next) => {
next();
};
});
this.httpServer = initServer();
});
after(function() {
this.httpServer.close();
});
describe("#POST /login", function() {
beforeEach(function() {
this.sandbox = sinon.createSandbox();
});
afterEach(function() {
this.sandbox.restore();
});
it("- should login in system and return data", async function() {
return chai
.request(this.httpServer.server)
.post("/logout")
.end((err, res) => {
expect(err).to.be.null;
expect(res).to.have.status(204);
});
});
});
});
initTestServer.js:
const isLoggedInMiddleware = require("./middleware");
const initServer = () => {
const express = require("express");
const app = express();
app.post("/logout", isLoggedInMiddleware.isLoggedIn(), (req, res, next) => {
return res.status(204).send();
});
const server = require("http").createServer(app);
server.listen(3004);
const close = () => {
server.close();
global.console.log(`Close test server connection on ${process.env.PORT}`);
};
return { server, close };
};
module.exports = initServer;
Thank you #EduardS for then answer!!
I solved it in a similar way:
it('Logout', async function(done) {
sinon.stub(helpers, 'isLoggedIn')
helpers.isLoggedIn.callsFake((req, res, next) => {
return (req, res, next) => {
next();
};
})
app = require('../index')
chai.request(app)
.post('/api/auth/logout')
.end(function(err, res2) {
expect(res2).to.have.status(204);
helpers.isLoggedIn.restore()
})
done();
});
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.
I have a middleware that I want to be applied only when the http method is post.
The following works fine, but I get the feeling there is a better way:
'use strict'
const express = require('express'),
router = express.Router()
router.use((req, res, next) => {
if (req.method === 'POST') {
// do stuff
}
return next()
})
module.exports = router
I'd like to do something like this, but it doesn't work:
'use strict'
const express = require('express'),
router = express.Router()
router.post((req, res, next) => {
// do stuff
return next()
})
module.exports = router
You can use * symbol:
const express = require('express')
const app = express();
app.post('*', (req, res, next) => {
console.log('POST happen')
next();
})
app.post('/foo', (req, res) => {
res.send('foo');
});
app.post('/bar', (req, res) => {
res.send('bar');
});
app.listen(11111);
This will respond with "foo" string on POST /foo and with "bar" string on POST /bar but always log "POST happen" to console.