I'm creating an restAPI and I'm not getting the express.Router () delete method to work
I'm looking for answers but all the answers I find are in line with what I did.
Only the delete method does not work, the others are OK.
Routes file:
const express = require('express')
const CustumerController = require('../controllers/CustumerController')
const router = express.Router()
router.get('/customers', CustumerController.index)
router.get('/customers/:id', CustumerController.show)
router.post('/customers', CustumerController.store)
router.delete('/customers/:id', CustumerController.destroy)
module.exports = router
CustumerController.js
async destroy(req, res) {
const { id } = req.params
const result = await CustomerRepository.delete(req)
res.json(result)
}
CustomerRepository.js
async destroy(id) {
const clause = {
where: { id }
}
try {
return await Customers.destroy(clause)
} catch (err) {
return {
statusCode: status.INTERNAL_SERVER_ERROR,
message: status.classes['5XX_MESSAGE'],
error: err
}
}
}
example of the error in delete:
example of the success, in get
I've tried, on the controller, to fire res.json () with a simple object, just for testing, but it didn't work, the request is not enough.
The problem seems to be really in the delete method of express.Router
I thank you for your help
the router is well programmed the problem has it in the endpoint response I recommend using try catch to analyze the error in detail
Everything is alright in your code, no need to change anything. debug it, first try to return static response on delete api instead of deleting a data from db. Then only you will get to know about the issue.
Your get request also should not work! You named your controller CostumerController in this line:
const CostumerController = require('../controllers/CostumerController')
And used CustumerController. I used your code and everything works correctly.
index.js
const express = require('express');
const router = require('./router');
const app = express();
app.use(router);
app.listen(8000, () => {
console.log('Server started.')
});
CustomerController.js
module.exports = {
destroy: (req, res) => {
res.status(200).send('destroy');
},
index: (req, res) => {
res.status(200).send('index');
},
show: (req, res) => {
res.status(200).send('show');
},
store: (req, res) => {
res.status(200).send('store');
}
}
And router.js
const express = require('express')
const CustomerController = require('./CustomerController')
const router = express.Router()
router.get('/customers', CustomerController.index)
router.get('/customers/:id', CustomerController.show)
router.post('/customers', CustomerController.store)
router.delete('/customers/:id', CustomerController.destroy)
module.exports = router;
Related
I have setup 3 different routes which have their own subroutes
router.use('/items', handleItems(app, router));
router.use('/price', handlePrice(app, router));
router.use('/documents', handleDocuments(app, router));
But when i call http://localhost:3000/api/documents/, it is throwing error as if it is calling functions inside /items routes. How can this happen?
After some more debugging i noticed that call is going inside handleItems handler to the /:id route
function handleItems(app, router) {
router.post('/create-one', itemController.createItem);
router.get('/:id', itemController.getItem);
return router;
}
Please refer to express.Router documentation.
In your example you have:
function handleItems(app, router) {
router.post('/create-one', itemController.createItem);
router.get('/:id', itemController.getItem);
return router;
}
The first parameter is not used, and the second parameter is your router object, so basically, you are registering your routes in the same object and you will end up with something like
/items
/create-one
/:id
/price
What you are looking for is something like:
const { Router } = require("express");
const express = require("express");
const app = express();
app.use("/items", handleItems(Router()));
app.use("/documents", handleDocuments(Router()));
function handleItems(r) {
r.get("/tada", (req, res) => res.json({ msg: `Tada` }));
r.get("/:name", (req, res) => res.json({ msg: `Hello ${req.params.name}` }));
return r;
}
function handleDocuments(r) {
r.get("/", (req, res) => res.json({ msg: "Bonjour" }));
return r;
}
app.listen(3000, () => console.log("🚀 Listening"));
In the example above, I registered a "new router" under each path (/items and /documents)
then, I registered each of these at their root paths:
app.use("/items", handleItems(Router()));
With:
function handleItems(r) {
router.post('/create-one', itemController.createItem);
router.get('/:id', itemController.getItem);
return r;
}
I don't know which architecture or what pattern you are using in your application, but if you are not using any you may want to use the following:
Create a routes.js file for each of items, price and documents. For example:
// items.routes.js
const { Router } = require("express");
const router = Router();
router.post('/create-one', itemController.createItem);
router.get('/:id', itemController.getItem);
module.exports = router;
Then in the app.js
const express = require("express");
const app = express();
// load routers
const itemsRoutes = require('./path/to/items.routes.js');
const documentsRoutes = require('./path/to/documents.routes.js');
app.use("/items", itemsRoutes);
app.use("/documents", documentsRoutes);
app.listen(3000, () => console.log("🚀 Listening"));
This might be a cleaner and more predictable pattern to work with in many cases
Follow the code below, everything is working fine with other collections that I am using exactly the same code and handlerFactory as well with the GET call for this one but when I try to delete and update it is getting a 404 error, it is not a typo because GET is working well and the same in the handlerFactory.js as all CRUD operations are working fine with this same code and other collection:
app.js
var userRouter = require('./routes/userRoutes');
// Routes Definitions
app.use('/', viewRouter);
app.use('/api/v1/aircraft', aircraftRouter);
app.use('/api/v1/users', userRouter);
UserRoutes.js
const express = require('express');
const userController = require('../controllers/userController');
const router = express.Router();
router.get('/', userController.getAllUsers);
router
.get('/:id', userController.getUser)
.delete(userController.deleteUser)
.patch(userController.UpdateUser);
module.exports = router;
UserController.js
const user = require('../models/userModel');
const factory = require('./handlerFactory');
exports.getAllUsers = factory.getAll(user);
exports.getUser = factory.getOne(user);
exports.deleteUser = factory.deleteOne(user)
exports.UpdateUser = factory.updateOne(user);
handlerfactory.js
const catchAsync = require('../utils/catchAsync');
const AppError = require('../utils/appError');
const APIFeatures = require('../utils/apiFeatures');
exports.deleteOne = (Model) =>
catchAsync(async (req, res, next) => {
const doc = await Model.findByIdAndDelete(req.params.id);
if (!doc) {
return next(new AppError('No document found with that ID', 401));
console.log('No document found with that ID');
}
res.status(204).json({
status: 'success',
data: null,
});
});
exports.updateOne = (Model) =>
catchAsync(async (req, res, next) => {
const doc = await Model.findByIdAndUpdate(req.params.id, req.body, {
new: true,
runValidators: true,
});
if (!doc) {
return next(new AppError('No document found with that ID', 404));
}
res.status(200).json({
status: 'success',
data: {
data: doc,
},
});
});
I will answers my own question here because Daniel just come to my attention a silly mistake that I was making but I think it can still be useful to others.
If you just wanna one route per line use:
router.update('/:id', userController.updateOne);
If you are trying to use multiple routes in the same line do not forget to use .route like in the code below:
router.route('/:id').get(userController.getUser)
.delete(userController.deleteUser)
.patch(userController.UpdateUser);
Thank you very much, it may be silly but silly things happen with code every day.
Thank you all!
Edit: I solved it. It was pretty simple, I had imported the routes before setting app.use for body-parser, so it didn't knew hot to parse JSON and thus returned an undefined body.
I'm trying to make a REST API following a tutorial. I made everything exactly as it was done in it, and tried every fix I could think of, but I can't make JSON POST requests work.
After sending a request I'm supposed to get a JSON like this:
{
"_id": "a13d1s2bc12as3a2",
"name": "Name",
"desc": "bla bla",
"_v": 0
}
But instead I'm only getting a 201 Resource with an empty body. Not even an empty object, just nothing.
I tried all the possible configurations in Postman and also HTTPie.
I also tried the suggested fixes changing body-parser config, since I got a clue something was deprecated or changed in the last months (json, urlencoded, etc.)
I checked if Mongoose was connected to the DB using the function it has for checking it (it was).
I have no clue where's the problem.
This is the index:
const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const meals = require("./routes/meals");
const orders = require("./routes/orders");
const app = express();
mongoose.connect(process.env.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true });
app.use("/meals", meals);
app.use("/orders", orders);
app.use(express.json());
app.use(bodyParser.json({ type: 'application/json' }));
module.exports = app;
The routes for Meals:
const express = require("express");
const Meals = require("../models/Meals");
const router = express.Router();
router.get("/", (req, res) => {
Meals.find()
.exec()
.then(x => res.status(200).send(x));
});
router.get("/:id", (req, res) => {
Meals.findById(req.params.id)
.exec()
.then(x => res.status(200).send(x));
router.post("/", (req, res) => {
Meals.create(req.body)
.then(x => res.status(201).send(x));
});
router.put("/:id", (req, res) => {
Meals.findOneAndUpdate(req.params.id, req.body)
.then(x => res.sendStatus(204));
});
router.delete("/:id", (req, res) => {
Meals.findOneAndDelete(req.params.id)
.exec()
.then(() => res.sendStatus(204));
});
module.exports = router;
And the model:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const Meals = mongoose.model("Meal", new Schema({
name: String,
desc: String
}));
module.exports = Meals;
Thank you.
To debug try this
const handleError = function() {
console.error(err);
// handle your error
};
router.post("/", (req, res) => {
Meals.create(req.body, function (err, req.body) {
if (err) return handleError(err);
})
});
You can also try to just provide static data for testing and see if that works, like :
const meal = new Meals({ name: 'some meal',desc: 'some desc' });
meal.save(function (err) {
if (err) return handleError(err);
// saved! return 200 or whatever you needs to do here
});
I solved it. It was pretty simple, I had imported the routes before setting app.use for body-parser, so it didn't knew hot to parse JSON and thus returned an undefined body.
app.use(bodyParser.json());
app.use("/meals", meals);
app.use("/orders", orders);
module.exports = app;
I need export an object or variable from my app to a router module.
The object is called "page" into "ClientClass".
I read something in SO and I tried to use a global var to save the object, exports it on end of unit.
This object will be used in the router module.
But, I have no success. In the router module "page" is always undefined.
How could I do that?
Main App JS - ClientClass.js:
const express = require('express');
const app = express();
const WARoutes = require('./routes/WARoutes');
var globalpage;
export default class ClientClass {
constructor(options) {
this.options = ...
}
async start() {
const browser = ....
const page = await browser.newPage();
// set globalpage to export
globalpage = page;
console.log('Done!');
app.use(express.json({ limit: '20mb' }));
app.use('/whats', WARoutes);
app.listen(port, () => {
console.log(`Listening on ${this.callbackUrl}...`);
});
}
start();
};
module.exports.page =globalpage;
WARoutes.js:
const express = require('express');
const router = express.Router();
const pagebrowser = require('../ClientClass.js');
const page = pagebrowser.page;
router.get('/getChats', async (req, res) => {
const chats = await page.evaluate((includePict, done) => {
do sometthing; //Here is my problem - page is undefined
}, includePict, done);
res.send(chats);
});
module.exports = router;
You have a cyclical dependency. You need to pass your page variable to the WARoutes.js implementation. Here's one way to do it:
WARoutes.js:
const express = require('express');
const router = express.Router();
//export a function which takes the `page` variable, *returning* the router which used to be _exported_
module.exports = function(page){
router.get('/getChats', async (req, res) => {
const chats = await page.evaluate((includePict, done) => {
do something;
}, includePict, done);
res.send(chats);
});
return router;
}
ClientClass.js:
const express = require('express');
const app = express();
const WARoutes = require('./routes/WARoutes');
export default class ClientClass {
constructor(options) {
this.options = ...
}
async start() {
const browser = ....
const page = await browser.newPage();
console.log('Done!');
app.use(express.json({ limit: '20mb' }));
app.use('/whats', WARoutes(page));
app.listen(port, () => {
console.log(`Listening on ${this.callbackUrl}...`);
});
}
start();
};
P.S.
I am also curious about what you're passing to page.evaluate. The first is a function with two arguments, the second and third are those two arguments again. I have a sneaking suspicion this is not going to work even as modified. You're going to need to provide more information about the page.evaluate API for additional help with that.
When my application starts, an object is created that is used throughout the app in multiple files. In my app.js file I am requiring several files that contain my routes. These routes need access to the object created in app.js so I'm trying to pass it to each of these files. An example of one of the files is below.
test.js
const express = require('express');
const router = express.Router();
router.use('/api', router);
router.post('/testRoute', (req, res, next) => {
if(globalVariable.abc) { //globalVariable is the variable created in app.js
//do some logic
}
res.json({
message: "Test Route Success"
})
});
module.exports = router;
app.js
const assessmentRoutes = require('./routes/test');
app.use(assessmentRoutes);
One way I've tried to get the variable passed in is wrapping the routes in test.js in a function that takes the variable like this.
module.exports = function(globalVariable) {
router.post('/testRoute', (req, res, next) => {
if(globalVariable.abc) { //globalVariable is the variable created in app.js
//do some logic
}
res.json({
message: "Test Route Success"
})
});
}
Then in app.js the require changes to:
const assessmentRoutes = require('./routes/assessments')(globalVariable);
When I do it this way I get errors starting the application like app.use() requires a middleware function.
How to I pass the object into my routes?
🙅 Using the global object
One approach, which I don't recommend, is using the global object:
app.js
const assessmentRoutes = require('./routes/test');
global.globalVariable = { abc: ... };
app.use(assessmentRoutes);
test.js
const express = require('express');
const router = express.Router();
router.post('/testRoute', (req, res, next) => {
if (globalVariable.abc) { ... }
res.json({
message: 'Test Route Success',
});
});
module.exports = router;
✨ Export a function and pass in options
Another approach is to export a function where you can pass in that as a param, as you did. The problem is that you are not returning the router from that function, that you later need to use in app.js:
app.js
const assessmentRoutes = require('./routes/test');
app.use(assessmentRoutes({ abc: ... }));
test.js
const express = require('express');
const router = express.Router();
module.exports = function(globalVariable) {
router.post('/testRoute', (req, res, next) => {
if (globalVariable.abc) { ... }
res.json({
message: 'Test Route Success',
});
});
// 👇 This is what you were missing in your code!
return router;
};
✨ Export the router and a setter function
A similar option would be to export the router object as well as a setter function to initialise globalVariable inside test.js:
app.js
const { assessmentRoutes, initAssessmentRoutes } = require('./routes/test');
initAssessmentRoutes({ abc: ... });
app.use(assessmentRoutes);
test.js
const express = require('express');
const router = express.Router();
let globalVariable = { abc: null };
function initAssessmentRoutes(options) {
globalVariable = options;
}
router.post('/testRoute', (req, res, next) => {
if (globalVariable.abc) { ... }
res.json({
message: 'Test Route Success',
});
});
module.exports = {
assessmentRoutes: router,
initAssessmentRoutes,
};
In your test.js get rid of:
router.use('/api', router);
In your app.js change this line:
app.use('/', assessmentRoutes);
This should works.
APP.JS
app.locals.globalVariable= {type:"Fiat", model:"500", color:"white"};
TEST.JS
if (req.app.locals.globalVariable.type =="Fiat" ) {
.....
}