I'm trying to add Sequelize transactions to my Express app, but I'm unsuccessful. I'm using async/await across the app, and I've created the namespace using the 'cls-hooked' package as instructed on the docs for Sequelize transactions.
Sequelize.useCLS(require('cls-hooked').createNamespace('db'));
My middleware is pretty simple and looks something like this
module.exports = () => (req, res, next) => sequelize.transaction(async () => next());
and in app.js
app.use(sequelizeTransaction());
app.use('/api', apiRoutes);
I've also tried using the middleware directly on routes, but I get the same result as above
router.post('/', sequelizeTransaction(), async (req, res) => {
await serviceThatDoesTwoDBOperations();
});
The result is that I get the transaction, but only around the first DB operation. Everything after that is ignored, and rollbacks aren't happening on errors. I'm probably doing something obviously wrong, but I can't put my finger on it.
I was having the same issue as you. This is how I made it work. For some reason sequelize was clearing the transaction on the namespace before it was actually completed.
Also make sure that you are using node > 8.5 for the async_hooks.
export const transactionMiddleware = async (req, res, next) => {
namespace.bindEmitter(req);
namespace.bindEmitter(res);
namespace.bind(next);
namespace.run(async () => {
const transaction = await sequelize.transaction();
namespace.set('transaction', transaction);
onFinished(res, (err) => {
if (!err) {
transaction.commit();
} else {
transaction.rollback();
}
});
next();
});
};
I hope that works for you :)
Related
I am currently working on node.js + express + mongoDB project. I am trying to handle error that occurs when data cannot be received from database. I am simulating this by terminating mongod process in console and calling .get in Postman. Sadly instead of getting an error in Postman I only get Unhandled Promise Rejection in console. I read a lot of posts about error handling and implemented it according to this guide: https://expressjs.com/en/guide/error-handling.html. I would be grateful for any idea of how can I fix this.
The code:
Printing all courses:
router.get("/", async (req, res, next) => {
try {
const courses = await Course.find().sort("dishName");
res.send(courses);
} catch (ex) {
next(ex);
}
});
error.js:
module.exports = function (err, res, req, next) {
res.status(500).send(`500 Error`);
};
index.js
const error = require(`./middleware/error`);
app.use(error);
app.use(error) is placed as the last app.use
There is a minor mistake in your code. The order of the req and res parameters in the error handler function should not be changed.
// Error.js
module.exports = function (err, req, res, next) {
res.status(500).send(`500 Error`);
};
My client given me requirement to encrypt decrypt all request response. So for all encrypted request we wrote down the express middleware to get decrypted request. which is the simple part but while sending response we also have to encrypt response.
One way to do write common function to encrypt data and call that function from all routes. which is time consuming part because we have more than 50+ routes in project. So i was thinking to write middleware like we have done for request which capture response before we send and then we encrypt response after that we send encrypt response to client.
I have searched for solution in google not got any proper solution which worked for me.
routes.js
router.post('/getUserData', verifyApiKey, async function (req, res, next) {
let user = await getUserData();
res.status(200).send(user)
});
middlware.js
class EncryptDecryptRequestResponse {
async encryptResponse(req, res, next) {
console.log('Called encryptResponse');
console.log('res.body', res.body);
res.body = encryptData(res.body)
next();
}
}
App.js
// Middleware to decrypt request
app.use(decryptRequest);
app.use('/', indexRouter);
// Middleware to encrypt response
app.use(encryptResponse);
but the problem is that i am not getting any console.log from middleware. this is the solution which i used
I tried to reproduce the problem you're having with overwriting res.send(), but it works fine for me. You need to make sure to setup the interceptor middleware before you define your routes. Consider this simple example:
const express = require('express');
const app = express();
function encryptResponseInterceptor(req, res, next) {
const originalSend = res.send;
res.send = function () {
arguments[0] = encryptResponse(arguments[0]);
originalSend.apply(res, arguments);
};
next();
}
function encryptResponse(originalData) {
// place your encryption logic here, I'm just adding a string in this example
return originalData + " modified";
}
// fake method that returns resolves after 1s just for testing
function getUserData() {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, 1000)
})
}
app.use(encryptResponseInterceptor);
app.get("/test", async (req, res, next) => {
await getUserData();
res.status(200).send("will be changed");
})
app.listen(3000, () => {
console.log("server started on 3000");
});
Forgive me for these heretical speeches, but I consider express to be the best library for api building from the developer experience point of view. But what stops me from using it everywhere is that everyone keeps saying (and confirming with benchmarks) that it is slow.
I try to choose an alternative for myself, but I canэt find what suits me.
For example with express I can simply organize the following structure:
userAuthMiddleware.js
export const userAuthMiddleware = (req, res, next) => {
console.log('user auth');
next();
};
adminAuthMiddleware.js
export const adminAuthMiddleware = (req, res, next) => {
console.log('admin auth');
next();
};
setUserRoutes.js
export const setUserRoutes = (router) => {
router.get('/news', (req, res) => res.send(['news1', 'news2']));
router.get('/news/:id', (req, res) => res.send(`news${req.params.id}`));
};
setAdminRoutes.js
export const setAdminRoutes = (router) => {
router.post('/news', (req, res) => res.send('created'));
router.put('/news/:id', (req, res) => res.send('uodated'));
};
userApi.js
imports...
const userApi = express.Router();
userApi.use(userAuthMiddleware);
// add handlers for '/movies', '/currency-rates', '/whatever'
setUserRoutes(userApi);
export default userApi;
server.js
imports...
const app = express();
app.use(bodyparser); // an example of middleware which will handle all requests at all. too lazy to come up with a custom
app.use('/user', userApi);
app.use('/admin', adminApi);
app.listen(3333, () => {
console.info(`Express server listening...`);
});
Now it is very easy for me to add handlers to different "zones", and these handlers will pass through the necessary middlewares. (For example users and admin authorization goes on fundamentally different logic). But this middlewares I add in one place and don't think about it anymore, it just works.
And here I am trying to organize a similar flexible routing structure on fastify. So far I haven't succeeded. Either the documentation is stingy, or I'm not attentive enough.
Fastify middlewares that added via 'use' gets req and res objects from the http library and not from the fastify library. Accordingly, it is not very convenient to use them - to pull something out of the body it will be a whole story.
Please give an example of routing in fastify a little more detailed than in the official documentation. For example similar to my example with user and admin on express.
I organize my routes like this:
fastify.register(
function(api, opts, done) {
api.addHook('preHandler', async (req, res) => {
//do something on api routes
if (res.sent) return //stop on error (like user authentication)
})
api.get('/hi', async () => {
return { hello: 'world' }
})
// only for authenticated users with role.
api.register(async role => {
role.addHook('preHandler', async (req, res) => {
// check role for all role routes
if (res.sent) return //stop on error
})
role.get('/my_profile', async () => {
return { hello: 'world' }
})
})
done()
},
{
prefix: '/api'
}
)
Now all request to api/* will be handled by fastify.
Lets look at the below modified Restify example which now includes Node 7/8s async/await support.
I have slight concerns on the proper implementation of this into express/restify/etc. My concern is with the promise hanging in the event loop for longer than it needs to... I know that this isn't exactly a promise, however should I be concerned about this implementation? I have yet to notice any issues.
'use strict';
const restify = require('restify');
const User = require('./models/User');
const server = restify.createServer({
name: 'myapp',
version: '1.0.0'
});
server.use(restify.acceptParser(server.acceptable));
server.use(restify.queryParser());
server.use(restify.bodyParser());
server.get('/echo/:name', async function (req, res, next) {
try {
const user = await User.findOne({
name: req.params.name;
});
res.send(user.get({plain: true}));
} catch (error) {
console.error(error);
res.send(500);
}
return next();
});
server.listen(8080, function () {
console.log('%s listening at %s', server.name, server.url);
});
There is a problem with using async function instead of regular functions accepting callbacks as the errors are handled differently.
In callback functions (aka "err-backs") the callback must be called regardless if the execution was successful. The first parameter is to be an error object.
The async function simply returns a rejected promise in case of any error (synchronous or asynchronous).
So by default, the Express.js/Restify expect a regular err-back. And if you pass the async function instead and it fails the Express.js/Restify will keep waiting for a callback to be called ignoring the rejected promise. It's simply not aware of a returned promise and not handling it.
Finally, the callback won't be called at all and the endpoint will timeout.
So you won't be able to handle the error properly.
You can try it out:
server.get('/echo/:name', async function (req, res, next) {
throw new Error();
});
So as a rule of thumb, I'd recommend not to mix the concepts and never pass callbacks into async functions. This is a red flag.
In order to fix this you need to use a wrapper like this for example:
const wrap = function(fn) {
return function(req, res, next) {
return fn(req, res, next).catch(function(err) {
return next(err);
});
};
};
server.get('/echo/:name', wrap(async function (req, res, next) {
throw new Error();
}));
You will get a proper status code and there will be no timeout anymore.
There are also a couple of modules you can use if you don't want to wrap it yourself:
Express.js: express-async-wrap
Restify: restify-async-wrap
I am writing the mocha unit test for the my express router.
I found that however I try to stub the middleware, it still execute the middleware code.
Here is my router & test, could anyone figure out?
Router:
import { aMiddleware, bMiddleware, cMiddleware } from '../middleware.js';
router.post('/url', aMiddleware, bMiddleware, cMiddleware, function(req, res) { ... }
Middleware:
AuthMiddleware.aMiddleware = async (req, res, next) => {
console.log('in real middleware');
next();
}
Test:
var authMiddleware = require('../../middleware/auth.js');
describe('Test', async () => {
before(function (done) {
_STUB_MIDDLEWARE_A = sinon.stub(authMiddleware, 'aMiddleware');
_STUB_MIDDLEWARE_A.callsArg(2);
}
after(function (done) {
_STUB_MIDDLEWARE_A.restore();
}
}
terminal will show the console.log('in real middleware') in middleware
This is likely because the stub happened after the module has been loaded already. You probably need to clear the cache first for your router file and then load it in again after the stubbing because es6 will cache the imported modules.