Passing Node.JS res to a function? - javascript

This doesn't show any response to an HTTP request, just hangs:
function middleware(req, res, next) {
to_end(res).NotFound();
return next();
}
This works:
function middleware(req, res, next) {
to_end(res).NotFound();
res.send(); // <----------------------------------------------
return next();
}
I'm using restify with this simple function:
export const to_end = res => {
return {
NotFound: (entity = 'Entity') => res.json(404, {
error: 'NotFound', error_message: `${entity} not found`
})
}
}

I'm not so good at ES6 but I guess that it may be the javascript's context problem (I mean the this variable).
In your case, res.json() may throw res is not defined (I guess) since the this at that moment is the context of export const to_end ... (thanks to =>'s magic), which does not know what is res.
Sorry for my terribly bad explanation :(. Read this to understand more about this.
We can make things simpler:
module.exports = function(res) {
return {
NotFound: function() {
res.json(404, {
error: 'NotFound',
error_message: `${entity} not found`
});
}
}
};
P/S: Not tested yet. Good luck! :)

Related

How to deal with async functions in express js router

There is a lint error, that basically says that we cannot return Promises in places where a void is expected, the message is clear, but how to fix it without lying to the linter by using any or (as any), both functions [validateJWT, getUser] are async functions It looks pretty basic, but I do not know how to fix it in an easy way. thanks!
import { Router } from 'express';
import { getUser } from '../controllers/userController';
import { validateJWT } from '../middlewares/validateJWT';
const router = Router();
router.get('/user', validateJWT, getUser);
export default router;
const getUser = async (req: Request, res: Response, next:
NextFunction): Promise<any> => {
try {
const { id } = req.params;
if (!id) {
let response = formatErrorResponse({ error: true, statusCode: 400, errorMessage: 'Missing id in params' });
return res.status(400).json(response);
}
let user = await User.findById({_id: id});
let objRes = { error: false, statusCode: 200, message: 'User was found', data: user };
return res.status(200).json(objRes);
} catch (error) {
console.log('error in controller /user', error)
next(error);
}
}
export {
getUser
}
const validateJWT = async (req: Request, res: Response, next: NextFunction): Promise<any> => {
const token = req.header('x-token');
console.log(req)
if (!token) {
const err = formatErrorResponse({ error: true, statusCode: 401, errorMessage: 'Missing header x-token' });
return res.status(401).json(err);
}
try {
await verifyToken(token);
next();
} catch (error) {
const err = formatErrorResponse({error: true, statusCode: 401, errorMessage: 'Invalid token, athorization denied', errorContent: error});
return res.status(400).json(err);
}
}
You need to change your implementation of the function to work like this:
router.get('/user', (req, res) => {
validateJWT(req, res);
getUser(req, res);
});
Since express.Router instances expect the route as a string for the first parameter which you have as "/user". And a second parameter which is a callback.
Inside that callback you can call your functions.
Assuming you need validateJWT to finish running before getUser you could do something like this:
validateJWT(...).then((...) => getUser(...));
I suggest something like the below.
router.use(validateJWT)
router.get('/users/:id', (req, res, next) => {
getUser(req.params.id)
.then(res.json)
.catch(next);
});
In the docs, I could see an async middleware example http://expressjs.com/en/guide/writing-middleware.html#mw-fig.
I could not find an async handler example. I recall that back in the day, express didn't support async handler. Hence, I used .then inside the handler. You need to double-check if you can use them nowadays.
In the express docs is also an example like the below. It makes working with async handler more convenient in some regard. As you can wrap all your async handler with this wrapper.
const wrap = fn => (...args) => fn(...args).catch(args[2]);
router.get('/users/:id', wrap(getUser));

How to unit test an Express Controller with Jest

I have been getting terribly confused with how to test my controller functions. I realize that I have to mock my dependencies, request, response, and the controller functions. Here's what I have so far:
OrdersController.js
const OrderService = require('../services/orderServices')
module.exports = class OrdersController {
static async apiGetOrders(req, res, next) {
try {
const orders = await OrderService.getOrders()
return res.status(200).json(orders)
} catch (error) {
return res.status(500).json({ error: 'Unable to get orders' }) // 500, Internal Service Error, generic
}
}
static async apiPostOrder(req, res, next) {
// All good, create an orderDocument
try {
const orderDocument = {
_id: null, // undefined at this point Mongo creates this _id for us
orderId: req.body.orderId,
cookies: req.body.cookies,
daySelected: req.body.daySelected,
timeSelected: req.body.timeSelected,
userInfo: req.body.userInfo,
createdAt: new Date(),
}
await OrderService.addOrder(orderDocument)
return res.status(201).send('success') // status OK, something was Created
} catch (error) {
return res.status(500).json({ error }) // 500, Internal Server Error
}
}
OrdersController.spec.js
import OrderService from '../services/orderServices'
import { mockOrder, mockOrders } from '../mocks/fixtures'
import OrdersController from '../controllers/ordersController'
jest.mock('../controllers/ordersController.js')
const mockRequest = () => {
return {}
}
const mockResponse = (mockOrders) => {
const res = {};
res.status = jest.fn().mockReturnValue(200);
res.json = jest.fn().mockReturnValue(mockOrders);
return res;
}
// #3 Test the OrdersControllers
// mock dependencies: req, res, and spyOn the controller functions
describe('Orders Controller', () => {
test('[Positive], should call OrderService.getOrders and receive status 200', async () => {
jest.spyOn(OrdersController, 'apiGetOrders')
const req = mockRequest()
const res = mockResponse(mockOrders)
await OrdersController.apiGetOrders(req, res)
expect(res.status).toHaveBeenCalledWith(200)
expect(res.json()).toEqual(mockOrders)
})
test('[Negative], error yields status 500', async () => {
jest.spyOn(OrdersController, 'apiGetOrders')
const req = mockRequest()
const res = mockResponse({status: 500, error: 'Unable to get orders'})
await OrdersController.apiGetOrders(req, res)
expect(res.status).toHaveBeenCalledWith(500)
expect(res.json()).toEqual(error)
})
})
I'm trying to test the happy path and the negative path on the get request. I followed this expample, https://codewithhugo.com/express-request-response-mocking/, and read all of the jest docs, https://jestjs.io/docs/mock-functions. The error that I receive is:
Questions:
Am I actually writing the tests correctly?
Am I also supposed to mock the OrderService?
Should I use Sinon or is Jest more than sufficient?
I am also new to Jest and am struggling with finding documentation that goes to enough detail to suggest to me what I'm doing wrong. But in your case, I think you might be spying on the wrong thing. The OrdersController is the subject of the test, so I don't believe that you should be mocking that. Rather you should spy on OrdersController's dependency, Orderservice and mock its methods.
Also, its not clear to me why you have next in
static async apiGetOrders(req, res, next)
You don't seem to use it in the body of the method anywhere, so hanging any testing off that value, probably won't work either.

Try catch block in express [duplicate]

This question already has answers here:
Handling errors in express async middleware
(6 answers)
Closed 3 years ago.
I google for it and found some examples (like this).
I done everything like there, and all just fine!
But now every my router function contains try...catch block. Like that:
accounts = express.Router()
accounts.post('/following', async (req, res, next) => {
try {
***do some stuff***
if (smth_bad)
next(new ErrorHandler(413, 400, "Wrong data"));
} catch (e) {
next(e)
}
});
accounts.post('/followers', async (req, res, next) => {
try {
***do some stuff***
if (smth_bad)
next(new ErrorHandler(413, 400, "Wrong data"));
} catch (e) {
next(e)
}
});
accounts.post('/posts', async (req, res, next) => {
try {
***do some stuff***
if (smth_bad)
next(new ErrorHandler(413, 400, "Wrong data"));
} catch (e) {
next(e)
}
});
accounts.post('/pizza', async (req, res, next) => {
try {
***do some stuff***
if (smth_bad)
next(new ErrorHandler(413, 400, "Wrong data"));
} catch (e) {
next(e)
}
});
app.use('/api/v1/account', accounts);
app.use((err, req, res, next) => {
handleError(err, res);
});
I know, that i can use next() without try...catch, but I want to handle unexpected errors and tell about it to user. My handling looks like this:
class ErrorHandler extends Error {
constructor(statusCode, httpStatus, message) {
super();
this.statusCode = statusCode;
this.httpStatus = httpStatus;
this.message = message;
}
}
const handleError = (err, res) => {
if(err instanceof ErrorHandler){
const { statusCode, message, httpStatus } = err;
res.status(httpStatus).json({
status: "error",
statusCode,
message
});
} else {
console.error(err);
res.status(500).json({
status: "error",
statusCode: '510',
message: 'Server error',
});
}
};
Is there a way to simplify try...catch blocks in every router?
First off, try/catch will not catch errors asynchronously. What you're doing WILL work if all your code inside the try/catch is synchronous, but as you've already discovered, that's a really cumbersome way to approach the problem.
There are quite a few ways you could go about this, but I think maybe you should be asking a different question: "What does good modular code look like?" or "How do I implement a router inside my code?"
I would suggest you go looking for patterns on how to structure a router.
One possible approach is to write functions that you define outside your try/catch blocks (possibly in another module, for tidiness's sake) and use them inside the try/catch. Like this:
const getStuffFunction = function(req,callback){
//do stuff here
}
accounts.post('/pizza', async (req, res, next) => {
getStuffFunction(req,function(){
//do router stuff here after your business logic is done
})
Hope that helps.

Function promise not returning anything after changed to async

I'm using paypal-node-SDK library to make calls to Paypal API. It uses a promise like this:
paypal.payment.create(create_payment_json, function (error, payment) {
if (error) {
throw error;
} else {
console.log("Create Payment Response");
console.log(payment);
}
});
However I'm trying to make it async because my other functions are async/await as well. But it doesn't return any callback, just undefined.
exports.create = wrap(async(req, res) => {
const payment = await paypal.payment.create(create_payment_json);
});
//wrap
module.exports = (fn) => {
return (req, res, next) => {
Promise.resolve(fn(req, res, next))
.catch((error) => {
console.log(error);
res.status(400).send({success: false, message: error.message});
});
};
};
It seems the library supports promises (feature: link), but the cb(null, response) doesn't really return anything when it is a async function. Am I missing something? Is there a way to make it work async?
You need beta / version 2.0+ to use promises in the sdk.
Not sure exactly what your wrap is, but for node styled callbacks function foo(a,b,callback) you can use promisify
const { promisify } = require('util');
exports.create = promisify(paypal.payment.create);
A manual conversion of paypal.payment.create would be
function create(create_payment_json){
return new Promise(function(resolve,reject){
paypal.payment.create(create_payment_json,function(error,payment){
if(error){
reject(error);
}else{
resolve(payment);
}
}
};
}
Which can then be used by const payment = await create(json);
Then in your router you can use something like
router.get('/', async function (req, res, next) {
try{
const payment = await create(json);
res.send(payment);
}catch(e){
console.log(e);
}
});
Extending on the answer by Cody G..
It is correct you need to upgrade to the beta v2+ to use promises in the sdk, although if you wish to do so, after you've upgraded you will find there are breaking changes.
You can read the full documentation here:
https://github.com/paypal/PayPal-node-SDK/tree/2.0-beta
There is also a migration guide to easily transition from v1 to v2:
https://github.com/paypal/PayPal-node-SDK/blob/2.0-beta/docs/Migrating.md

Optimize API requests in Node.js

I have an API in Node.js. My routes look like
exports.getUserList = (req, res, next) => {
User.find().sort({ name: 1 }).then(users => {
res.json({
status: 'success',
users
});
}).catch(next);
};
As seen in the example, I use .catch(next). But is this the correct way to do it? Shouldn't the route always print json?
So I am thinking of doing something like
exports.getUserList = (req, res, next) => {
User.find().sort({ name: 1 }).then(users => {
res.json({
status: 'success',
users
});
}).catch(err => {
res.json({
status: 'error',
msg: err
});
});
};
but shouldn't it then be something like res.status(some_status_code).json({})?
How is a simple API normally carried out in terms of error handling?
What if I, in the code, use a variable that is not defined (i.e. causing a syntax error)? Should I handle it with a JSON error or should I just make sure that I don't do sloppy coding? :-D
Also, is this the fastest way to print the json? I mean, should I use User.find().lean()? Should I do some caching? Is it even clever to store my API on a normal website or are there optimized API servers for such cases?
Have you try async/await function and custom response for success and error?
Here the example :
responseHandler.js
// use this for success
function successResponse(message, data) {
const success = {
success: true,
message: message,
data: data
}
return success
}
// use this as middleware error handler
function errorResponse(err,req,res,next) => {
return res.status(err.status || 500).json({
success: false,
message: err.message
})
}
module.exports = {
successResponse,
errorResponse
}
myRouter.js
const { successResponse } = require('./responseHandler')
exports.getUserList = async (req, res, next) => {
await User.find().sort({ name: 1 }).then(users => {
res.status(200).json(
successResponse(`data user`, users)
)
}).catch(err => {
return err
});
};

Categories