I am facing issue I am testing an api using chai-http but it struck in middlewear means it does not call next() function. Not sure how to handle this, little guide may be helps me alot. Here is my code may be it helps you understand better.
Middleware
function middleware(req, res, next) => {
// doing some stuff
console.log("step 1") // prints
next()
}
API
router.get('/orders', middleware, (req, res) => {
// some stuff
console.log("step 2") // not prints
res.send(result)
})
Test Case
let order = new Order(mockOrder);
order.save((err, order) => {
chai.request(server)
.get('/orders')
.end((err, res) => {
// some stuff
})
})
This update might be useful for you.It is from github.
Another large breaking change is that the chai.request(server) object will automatically close the server connection after the first request. This means if you have code like this:
you'll need to change it to:
const request = chai.request(app).keepOpen()
Related
Let's say I have the following Next.js api route.
/api/protected-api
This api will get a authorization: "Bearer: TOKEN" header for authorization purposes.
import { NextApiHandler } from "next";
const apiHandler: NextApiHandler = async (req, res) => {
await runAuthMiddleware(req,res);
// THE REST OF THE API LOGIC SHOULD ONLY RUN IF AUTHORIZATION SUCCEEDS
// IN THEORY, runAuthMiddleware WOULD RESPOND WITH 403 IF AUTHORIZATION FAILS
return res.json(data);
};
What I mean by the code above is:
If authorization fails, I would like to respond a 403 from the runAuthMiddleware function, and don't even bother running the rest of the code in the apiHandler.
Is this even possible? Is this an anti-pattern?
Should I got with something like this instead?
const apiHandler: NextApiHandler = async (req, res) => {
const authSuccess = await runAuthMiddleware(req,res);
if (authSuccess)
return res.json(data);
else
return res.status(403).send("Forbidden");
};
UPDATE:
It seems that there's no easy way of doing it. This package offers a possible solution: next-api-middleware
There is an easy way to do it if that is the only thing that you need, just make your own higher order function which checks auth:
const withAuth = (f) => async (req, res) => {
const isLogged = await checkIfUserIsLogged(req)
if (isLogged) {
return f(req, res)
} else {
return res.status(403).send("Forbidden");
}
}
const apiHandler = withAuth(async (req, res) => {
// This code now only runs of the user is logged
return res.json(data);
});
For more complex scenarios I would recommend to use https://github.com/hoangvvo/next-connect or something like that. But if you only need one middleware then it is completely fine to write your own.
From inside my expressJS application I have to verify that a cookie token is valid with a back-end server. So the relevant code involved in this is as follows:
app.get('*', (req, res, next) => {
console.log('GET: ' + req.path);
// ...
const payload = JSON.stringify({ authnToken: token });
const opts = { ... authServerOptions };
opts.headers['Content-Length'] = payload.length;
// build request
const restReq = https.request(authServerOptions, result => {
console.log('back-end response' + result.statusCode);
result.on('data', data => {
next(); // token is good now proceed.
});
result.on('error', error => {
res.redirect('somewhere'); // token is bad or timeout
});
});
restReq.write(token);
restReq.end();
}
So the main get function sets the REST request in motion and then just returns without calling next() or anything.
Questions:
Is this the right code for doing this? What happens if the callbacks are never called?
Is the application blocked from processing other requests until the back-end server returns or times out?
If so is there some way of freeing up the thread to process more requests?
Thanks in advance for any help. I haven't found many examples for this code pattern so if there is one a link would be appreciated.
Yes, I think the general idea of your implementation is correct.
I would also suggest, as done in the comments, to use a client such as axios to handle the request in a less verbose and more comprehensive manner, which would leave your code looking something like this:
const axios = require('axios');
app.get('*', (req, res, next) => {
const payload = JSON.stringify({ authnToken: token });
const opts = { ... authServerOptions };
opts.headers['Content-Length'] = payload.length;
axios.post(url, payload, opts)
.then(response => next())
.catch(error => {
console.error(error);
res.redirect('somewhere');
});
});
A bit more to the point, but functionally almost equivalent to your implementation. The one thing you are missing is the onerror callback for your request object, which currently may fail and never return a response as you correctly suspected. You should add:
restReq.on('error', error => {
console.error(error);
res.redirect('somewhere');
});
On the same vein, it would probably be more fitting to call next on result end, instead of doing so while reading response data:
result.on('end', () => {
next();
});
Then you'd be covered to guarantee that a callback would be invoked.
Neither implementation blocks the processing of future requests, as the call to the token validation service is done asynchronously in both cases.
I am writing the middleware for API endpoints in my app that respond to webhooks from other applications, and am relatively new to Koa, so am not completely familiar with its patterns.
I would like to structure my middleware as follows:
exports.updateReceived = async (ctx, next) => {
// Respond to server issuing the webhook
ctx.res.body = "ok";
ctx.res.statusCode = 200;
// Grab what we need from the request
const { headers, state, request } = ctx;
const { body } = request;
// Do some async work
const { example } = await doSomeAsyncWork(ctx);
// Prepare a database query
const query = { aValue: anId };
// Run the DB query
const result = await Thing.findOne(query);
// Add data to the request
state.thing = result;
// Move on...
return next();
};
However, this does not appear to be working, as an error in any of my async methods can cause the route to error out.
My goal is for this endpoint to always respond "yep, ok" (immediately), meaning it is simply up to the application to handle any error states.
I have researched this fairly well, and have come across this pattern:
app.use(async ctx => {
db.fetch() // Assuming a Promise is returned
.then(() => { ... })
.catch(err => {
log(err)
})
// status 200 will be returned regardless of if db.fetch() resolves or rejects.
ctx.status = 200
})
However, this does not meet my needs as the middleware makes no use of next, so it is not really a useful pattern, so far as I can tell.
Could someone tell me what I am overlooking?
next() invokes the downstream middleware chain and returns a promise that resolves after all downstream middleware/handlers have finished.
That means you can simply implement your own upstream error handler that catches any errors and always ensures a 200 OK response.
const Koa = require('koa')
const app = new Koa()
app.use(async (ctx, next) => {
next().catch((err) => {
// Print error for our own records / debugging
console.error(err)
// But ensure that outgoing response is always a smile
ctx.status = 200
ctx.body = ':)'
})
})
app.use(async (ctx) => {
// Do your webhook / biz logic here, but for demonstration
// let's make it always throw an error. Thus upstream next()
// will be a rejected promise.
throw new Error('this middleware will always bubble up a rejected promise')
})
app.listen(3000, () => {
console.log('listening on 3000')
})
Note: We are not awaiting next(), so we can end the request immediately. However the next handler in the chain will still have the opportunity to process the logic
app.use((ctx, next) => {
next()
ctx.status = 200
})
app.use( async ctx =>{
db.fetch()
.then(() => { ... })
.catch(err => log(err))
}
}
Just to divert the solution in a different side, You could consider adding your work to some kind of MessageQueue and then let another process do that task for you. Basically asynchrously but you will still be important. This kind of pattern suits for your requirement.
There are many messaging system availble like AWS SQS which you could consider. This way your api will be very light weight and it will do thing which it needs to and send a command to your messaging system to do extra stuff. You are basically separting your core logic and the doing things in background which scales very nicely as well.
Using Mocha/Chai for REST API unit testing, I need to be able to mock req.session.someKey for a few of the end points. How can I go about mocking req.session?
I'm working on writing REST API unit tests for a NodeJS Express app that utilizes express-session. Some of these endpoints require the use of data stored in req.session.someKey, the endpoint is setup to return a 400 if req.session.someKey is undefined so I need to be able to mock it in order for the test to complete successfully.
Example code:
router.get('/api/fileSystems', utilities.apiAuth, (req, res) => {
let customer = req.session.customer;
let route = (customer === 'NONE') ? undefined : customer;
if(route == undefined){
res.status(400).send('Can't have customer of undefined');
} else {
let requestOptions = setRequestOptions(route);
queryFileSystemInfo(requestOptions, (info) => {
res.status(200).send(info);
});
}
});
What I've tried:
describe('/GET /api/fileSystems', () => {
it('It should return information about the filesystem for a customer'), (done) => {
chai.request(server)
.get('api/fileSystems')
.set('customer', '146')
.end((err, res) => {
res.should.have.status(200);
done();
});
});
});
I attempted to use the .set() in order to set req.session but I believe that .set just sets the headers so I don't believe that I can update it that way unless I'm missing something.
In your express setup you usually plug in the session middleware like this
app.use(session(config))
instead you can put the session middleware in a handy accessible location, and make a wrapper for it, like this:
app.set('sessionMiddleware') = session(config)
app.use((...args) => app.get('sessionMiddleware')(...args)
Tests will need access to the express instance, you can do this by refactoring /app.js to export a function.
function app () {
const app = express()
// ... set up express
return app
}
// run app if module called from cli like `node app.js`
if (require.main === module) instance = app()
module.exports = app
Then in your test, you can overwrite app.sessionMiddleware
describe('/GET /api/fileSystems', () => {
it('It should return information about the filesystem for a customer'), (done) => {
app.set('sessionMiddleware') = (req, res, next) => {
req.session = mockSession // whatever you want here
next()
}
chai.request(server)
.get('api/fileSystems')
.set('customer', '146')
.end((err, res) => {
res.should.have.status(200);
done();
});
// then you can easily run assertions against your mock
chai.assert.equal(mockSession.value, value)
});
});
The other options I've seen on the net involve setting a cookie to match a session which is stored in the db, the problem with that approach is that you end up running into problems when the session in the db expires, so tests fail over time as fixtures become stale. With the approach outlined above you can work around that by setting expiries in the test.
mock-session is pretty use full to mock your session object
let mockSession = require('mock-session');
describe('/GET /api/fileSystems', () => {
it('It should return information about the filesystem for a customer'), (done) => {
let cookie = mockSession('my-session', 'my-secret', {"count":1}); // my-secret is you session secret key.
chai.request(server)
.get('api/fileSystems')
.set('cookie',[cookie])
.end((err, res) => {
res.should.have.status(200);
done();
});
});
});
For this project, I ended up having to set req.session.customer in our server.js file that has an app.use() call that uses a middleware function to set the current session. I was unable to actually find a package that directly mutates the req.session object at test time.
app.route('/users')
.post(user.post)
.get(user.get)
.get(user.everyone)
.put(user.update)
.delete(user.delete);
I have ran into the problem of my function using two res.send, so I am getting the 'Error: cannot set header after they are sent.' error, to fix this I have turned it into two functions which I am trying to use two .get on the app.route, but it seems I can only use one as when I use two the second one doesn't work.
Is there a way I could use two .get on one app.route?
If not, what are my options to get around this problem?
You need to create separate routes for each api endpoint like this:
app.route('/users').get(req, res) => {
//Get all users
});
app.route('/users:/id').get(req, res) => {
//Get specific user
});
app.route('/users').post(req, res) => {
//Create new user
});
app.route('/users/:id').put(req, res) => {
//Update user
});
app.route('/users/:id').delete(req, res) => {
//Delete user
});
Once res.send is called, means your server has sent the response to the browser or whatever. you can't change the already sent response and its header.
You can use multiple callbacks on one route and one method(post,get)
An array of callback functions can handle a route. For example:
var cb0 = function (req, res, next) {
console.log('CB0')
next()
}
var cb1 = function (req, res, next) {
console.log('CB1')
next()
}
var cb2 = function (req, res) {
res.send('Hello from C!')
}
app.get('/example/c', [cb0, cb1, cb2])
yes, you can use multiple HTTP requests either its .get or .post but with different params. or routes.