How to organize routing in fastify? - node.js

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.

Related

Express-validator input sanitize inside a middleware or router callback?

I'm trying to improve the security of my express app by using the express-validator package, I figured out that I could use it in two different ways.
The first inside a middleware:
const {check}=require("express-validator");
app.post(
"/random-post",
[
check("email").isEmail(),
],
(req, res) => {
//some code
})
Second inside of the router's call back:
app.post("/random-post",(req,res,next)=>{
check(req.body.email).isEmail()
})
Which of the two are most used or maybe should I use both?
I think the best practice here is to do the validation and sanitazion before reaching the controller. In the same block of logic you can also check if there are any validation errors and return an error. This way the controller is never reached, so code in your controller is never executed. I updated your example below:
const {check} = require('express-validator');
app.post(
'/random-post', [
check('email').isEmail(),
(req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({
errors: errors.array()
});
}
next();
}
],
(req, res) => {
//some code in your controller
}
);

How to duplicate and forward a request with koa router

For several reasons, I have a server that has to forward requests to another server. The response should be the response of the final server. I also need to add an extra header onto the request but remove this header again from the response before returning. As such, redirect isn't going to cut it.
I'm currently doing it manually copying the headers & body as required but I would like to know if there's a simple generic way to do it?
A proxy would work for this. Assuming #koa/router or something simliar and the http-proxy module (there are also wrapper modules for Koa that may work:
const proxy = httpProxy.createProxyServer({
target: 'https://some-other-server.com',
// other options, see https://www.npmjs.com/package/http-proxy
})
proxy.on('proxyReq', (proxyReq, req, res, options) => {
proxyReq.setHeader('x-foo', 'bar')
})
proxy.on('proxyRes', (proxyRes, req, res) => {
proxyRes.removeHeader('x-foo')
})
router.get('/foo', async (ctx) => {
// ctx.req and ctx.res are the Node req and res, not Koa objects
proxy.web(ctx.req, ctx.res, {
// other options, see docs
})
})
You could also lift the proxy out of a route if you happen to be starting your Koa server with http.createServer rather than app.listen:
// where app = new Koa()
const handler = app.callback()
http.createServer((req, res) => {
if (req.url === '/foo') {
return proxy.web(req, res, options)
}
return handler(req, res)
})

How to write middleware to modify response in node js

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");
});

Sequelize transactions as Express middleware

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 :)

Best practices for building singletons in node.js

I am trying to build an express app and I need to create some singletons (like db object in Sequelizer).
app.js
app.use(...);
app.use(...);
var serviceLocator = {
foo: require('foo'),
bar: require('bar')
}; //object holding singletons.
app.use('/api/todo', new todoRoutes(serviceLocator));
todoRoutes.js
module.exports = (serviceLocator) => {
var router = express.Router();
router.get('/', (req,res,next) => {
//use serviceLocator.foo
});
router.get('/:id',(req,res,next) => {
//use serviceLocator.bar
});
};
Is this a good practice?
(I've also read about building singletons using require caching, but I have concerns since in the official docs they say that require "may not" return the same object).
How I usually do it looks something like this:
app.js
const db = require('./path/to/db/singleton');
const sql = require('./path/to/Sequelizer/singleton');
app.use(...);
app.use((req, res, next) => {
req.db = db;
next();
});
app.use((req, res, next) => {
req.sql = sql;
next();
});
app.use('/api/todo', require('./todoRoutes');
todoRoutes.js
const express = require('express');
const router = express.Router();
router.get('/', (req,res,next) => {
console.log(req.db);
});
router.get('/:id',(req,res,next) => {
console.log(req.sql);
});
module.exports = router;
The overall is just to add some middleware that adds it to the req, which is going to go through the pipeline for you. You could namespace it too by adding it to req.server.<thing> or something.
You can also do this to generate a request ID by bringing in uuid and attaching an ID to every req in another middleware that you can use in your log statements later, that way you can track requests through.
Alternatively, just require those singletons into the todoRoutes.js and use them directly. They're singletons on your server, so I don't see any issue with that.

Resources