How to use next() function in Next JS API like Express JS? - javascript

While making an API in Next JS, where I pass three parameter to my API function (req, res, next) here is the code :
import catchAsyncErrors from "../../../middleware/catchAsyncErrors.js";
import connectToMongo from "../../../middleware/db.js";
import isAuthenticatedUser from "../../../middleware/isAuthenticated";
import ErrorHandler from "../../../utils/errorHandler";
const handler = catchAsyncErrors(async (req, res, next) => {
try {
console.log(next); // Undefined
if (req.method === "POST") {
return next(new ErrorHandler("Not Allowed", 405));
// ^ Error
} else {
return res.status(500).send("INVALID REQUEST");
}
} catch (error) {
console.log(error);
}
});
export default connectToMongo(isAuthenticatedUser(handler));
I assure that ErrorHandler is working fine, but when I call this API it gives me the following Error:
TypeError: next is not a function
at eval (webpack-internal:///(api)/./pages/api/auth/test.js:19:20)
at eval (webpack-internal:///(api)/./middleware/catchAsyncErrors.js:3:25)
at eval (webpack-internal:///(api)/./middleware/isAuthenticated.js:22:16)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
In Express JS we pass next parameter to call next function or error handler but it seems like it doesn't work with Next JS. I am new in this framework so I need your help in this.

So I found that we cannot use next() function in next JS like we used to do in express JS. Next JS API function accept only two parameters (req, res).
To use next() function to call other middleware, check out the bellow example :
import middleware1 from './middleware/func1';
import middleware2 from './middleware/func2';
const handler = async(req, res)=>{
//Your Code here
}
export default middleware1(middleware2(handler));
In the middleware functions:
middleware 1 :
const middleware1 = (handler) => {
return (req, res) =>{
//middleware function here
return handler(req, res)
}
}
export default middleware1;
middleware 2 :
const middleware2 = (handler) => {
return (req, res) =>{
//middleware function here
return handler(req, res)
}
}
export default middleware2;
As you can see both middleware take handler in parameter and return handler at end of code.
Make sure to return handler(req, res) at the end of the function as given in code

Related

importing the request in expressjs

I'm creating a helper method in my code which I can use for authorizing users,
I am creating a function called "usePermissions" following the react way but on the back-end.
The usePermissions() will return true or false based on some x, y and z.
But this method depends on the request object,
so in order to make it work, I'll everywhere have to call
usePermissions(req),
ex:
import usePermissions from 'hooks/usePermissions'
usersRouter.delete('/users/:userId', async (req, res, next)=>{
const { verify } = usePermissions(req)
if(verify('deleteUsers')) await UserModel.delete(req.params.userId)
// ...
})
Is there a way to package this "usePermissions" helper method with the request object?
I want it to automatically have the request object, I don't want to keep passing it as a variable,
how to make it have the request object without having to pass it as an argument to it, is it possible?
ex:
import usePermissions from 'hooks/usePermissions'
usersRouter.delete('/users/:userId', async (req, res, next)=>{
const { verify } = usePermissions() // <-- see, I want to not pass the req object
if(verify('deleteUsers')) await UserModel.delete(req.params.userId)
// ...
})
You can create a middleware that will call usePermissions and append result to the Request object, so it will become available in all your handlers without explicitly calling it.
Your middleware code might look something like (read more about using middlewares in Express app)
export function getUsePermissions = (req, res, next) => {
const { verify } = usePermissions(req);
req['verify'] = verify
// moving to the next middleware in chain, now Request object has a verify property
return next()
}
and in your express app, add getUsePermissions middleware
express.use(getUsePermissions);
now you can use extract usePermissions from request object in your handlers:
usersRouter.delete('/users/:userId', async (req, res, next)=>{
const { verify } = req['verify'];
if(verify('deleteUsers')) await UserModel.delete(req.params.userId)
// ...
})
Yes, you can use it as middleware and you will get a response, request and next object by default.
import usePermissions from 'hooks/usePermissions'
usersRouter.delete('/users/:userId', usePermissions , async (req, res, next)=>{
if(verify('deleteUsers')) await UserModel.delete(req.params.userId)
// ...
})
usePermissions Function will be like this:
const usePermissions = (request, response, next) => {
const isPermitted = 'you logic';
if (isPermitted) {
next(); // this will automatically call the next middleware
// in your case it will call the next callback in the userRouter
} else {
response.send('You don't have permission'); // just added a sample text for brevity
}
}
You can read more about middlewares here.

Javascript - Error: Route.post() requires a callback function but got a [object Promise]

One of my express routes is giving me the following error:
Error: Route.post() requires a callback function but got a [object Promise]
It's because of the last line in controllerFunction:
// IN CONTROLLER.JS
exports.controllerFunction = async (req, res) => {
//Get some values
let foos = await User.somefunction();
for(foo in foos){
//Call some async void functions
}
res.status(200).send('DONE')
}
...
// IN ROUTER.JS
router.post('/', validation, validate, checkLoggedIn, checkUserPermission(1), async(req, res, next) => {
try{
await controller.controllerFunction(req, res)
} catch (error) {
next(error);
}
});
I figured I need to return a callback function, but I'm not sure how. The rest of my controller functions look like that and they work because of the .then():
// IN CONTROLLER.JS
exports.getCameras = async (req, res) => {
await Model.getCameras(req.machine)
.then(cameras => res.send(cameras));
}
But in my controllerFunction I can't call .then() due to the fact that I need to do some logic beforehand in a for loop, so I need to generate the callback function in some other way.
How could I make controllerFunction a callback function?
try this.
return res.status(200).send('DONE');
exports.controllerFunction = async (req, res) => {
//Get some values
let foos = await User.somefunction();
for(foo in foos){
//Call some async void functions
}
return res.status(200).send('DONE');
}
As #slebetman stated in the comments, one of the functions called in the router was incorrect:
router.post('/', validation, validate, checkLoggedIn, checkUserPermission(1), async(req, res, next) => {}
Make sure validation, validate and checkLoggedIn are all functions and not a return value
checkUserPermission(1) was incorrect since that function wasn't expecting any parameters to be passed, changed to checkUserPermission and it worked.

Error: Route.get() requires a callback function but got a [object Undefined] while using imported function

I am checking if a user is logged in a route called forum. I am importing it like this.
The file is routes/forum.js
const isloggedinimport = require('../index')
I have the function on index.js
const isloggedin = (req,res,next) => {
if(req.user) {
next()
}
else {
res.render('loginerror',)
}
}
I am exporting with
module.exports = isloggedin
When I try to run this
router.get('/', isloggedinimport.isloggedin, (req, res) => {
res.render('monitors/monitorhome')
});
module.exports = router
I get the error that Route.get() requires a callback function but got a [object Undefined]
The error is on this line
router.get('/', isloggedinimport.isloggedin, (req, res) => {
How do I fix this?
When exporting the function, try using the following code:
module.exports.isloggedin = isloggedin
This will set the property isloggedin to the function so that when you call isloggedinimport.isloggedin, it will access the function properly. Alternatively, you could use the following code to export your function:
module.exports = isloggedin
and then use this code to import the function:
const isloggedin = require('../index')
...
router.get('/', isloggedin, (req, res) => {
res.render('monitors/monitorhome')
});
Had the same problem and fixed it by adding brackets around the function you want to export:
module.exports = {isloggedin};

Where does next() go in Express js?

I'm new to javascript, nodejs, and express, and confused of using next().
I want my code to move on to the next router with next(), but it seems to move on to the next then.
My code:
//validation
router.post('/requests', (req, res, next) => {
let {myData} = req.body
basicCheck(res, cluster, myData)
.then(() => {
if (myCheck()) {
next()
return // Doesn't need to do rest of the code. Just move on to the next router.post
}
...
return Promise.all(somePromises)
})
.then(() => {
...
return Promise.all(somePromises)
})
.then(() => {
if (someCheck() {
next()
} else {
res.status(400).send('message') // My code reaches here! even when myCheck() is true
}
})
.catch((err) => {
...
})
})
// where next() needs to be
router.post('/requests', (req, res) => {
...
})
When next() is outside the basicCheck, next() goes to the next router.post.
I don't get the concept of where next() indicates.
How can I correct this code while doing myCheck() inside basicCheck()?
With next() you move to the next middleware.
Exapmle:
You have a route like:
app.get("/", (req, res, next) => {
res.send("hello")
})
Instead of using an anonymous function you can declare an function and use it it like:
function firstMiddleware(req, res, next){
res.send("hello")
}
app.get("/", firstMiddleware);
What you can do is you can have multiple middlewares in your route like:
function firstMiddleware(req, res, next){
console.log("hy");
next()
}
function secondMiddleware(req,res,next) {
console.log("hello")
res.send("hello");
}
app.get("/", firstMiddleware, secondMiddleware);
As you can see. In my first middleware i use next(). This tells express.js to move to the next middleware in this case secondMiddleware
The middlewares gets executed from the left to right and with next() you tell them to move to the next until you are on the end.
Usually the last middleware is your API endpoint and you should not use next() otherwise you would "jump out" of your route and you would receive an error if you have defined an global error handler
Also sidenote: A bonus would be to seperate your routes and logic by creating an file called controller.js for example.
controller.js
function firstMiddleware(req, res, next){
console.log("hy");
next()
}
function secondMiddleware(req,res,next) {
console.log("hello")
res.send("hello");
}
module.exports = {
firstMiddleware,
secondMiddleware
}
Now you can import it:
const { firstMiddleware, secondMiddleware } = require("./controller.js");
app.get("/", firstMiddleware, secondMiddleware);
This makes your code easier to maintain as it grows
EDIT:
router.post("/requests", async (req, res, next) => {
let { myData } = req.body;
let checkresult = await awbasicCheck(res, cluster, myData);
if (myCheck()) {
return next();
}
let someResults = await Promise.all(somePromises);
let someMoreResults = await Promise.all(somePromises);
if (someCheck()) {
return next();
} else {
res.status(400).send("message"); // My code reaches here! even when myCheck() is true
}
});
You use return witch yes stops the function from execution BUT what you also do is an promise chaining.
I have written here an async / await approach

Async wrap function for express 4 doesn't catch error

I am trying out this trick from strongloop https://strongloop.com/strongblog/async-error-handling-expressjs-es7-promises-generators/ for having a wrap function for async routes
but the error function is never called. I also tried to put the error function inside the authRouter file.
in authRouter.js:
let wrap = fn => (...args) => fn(...args).catch(args[2]);
router.post('/login', wrap(async (req,res) => {
if (!req.body.email || !req.body.password) throw new Errors.BadRequestError();
}));
export default router;
and in app.js
app.use('/auth', authRouter);
app.use(function(err, req, res) {
console.log('in here');
const status = err.status || 500;
if (status === 500) console.log(err);
res.status(status);
res.send({
message: err.message,
error: err
});
});
You need to have 4 parameters in the error handler to make express recognise it as one:
(from http://expressjs.com/en/guide/error-handling.html): "Define error-handling middleware functions in the same way as other middleware functions, except error-handling functions have four arguments instead of three: (err, req, res, next)."
this is the last error handler I don't wanna call next()
That doesn't really matter, you still have to declare it even if you don't use it.

Categories

Resources