Fastify: avoiding writing boiler plate handler in every routes - fastify

Is there a possibility in Fastify to avoid writing the similar handler always?
My handler looks like (a proxy sort of):
handler: async (request, reply) => {
reply.from(proxyMap[request.url]);
return reply;
}
where the proxyMap is just a mapping of my proxy routes to target/upstream route path. However the handler is always same.
Say a sample proxyMap content can be:
const proxyMap = {
"/api/path1/proxy1" : "/api/backend/api1",
"/api/path2/proxy2" : "/api/backend/api2",
....
};
How can I move to handler somewhere common (say in some lifecycle hook or somewhere appropriate) such that every route I need not to write it. Like when I will write the route, it will be executed based on the proxyMap look up.
Is there a way in fastify to achieve that?

You can automate it with some coding:
async function handler(request, reply) {
reply.from(proxyMap[request.url])
return reply
}
const proxyMap = {
'/api/path1/proxy1': '/api/backend/api1',
'/api/path2/proxy2': '/api/backend/api2',
}
Object.keys(proxyMap).forEach((url) => {
fastify.get(url, { handler })
})

Related

Simulating request error with async function

In my express app, I declare request handlers like this(simplified here):
export const CreateProduct = async (req, res, next) => {
try {
// ProductModel is a sequelize defined model
const product = await ProductModel.create(req.body)
res.send(product)
} catch (err) {
// I have raygun setup as the error handler for express
// in this example, it will finally return 500
next(err)
}
}
And then use it like so:
import { CreateProduct } from './create_product'
export const ProductRouter = express.Router()
ProductRouter.post('/', CreateProduct)
However, when running my test, nyc/istanbul will complain that line 9 is an Uncovered Line (in my example, it is the next(err) function), how do I go about simulating the error in my example?
The easier to go is to create a validation mechanism for your ProductModel, and when you create the product with invalid data throw some validation errors.
And in your mocha you will send an invalid product body and it will catch your next.
Two of the easiest possible ways of doing this are as follows:
1) Admit that controllers can not be unit tested, and because they can not be unit tested, they will be a part of your integration tests, and because that is true, you are going to move as much unit testable code out of them as physically possible, and limit them to doing little more than composing pieces of code which are able to be tested. This would be fine; an Express controller is sort of like a main method into your application, from the view of an individual request, anyway, so having it mix together the hard concepts to decide what gets built is okay, as long as the main isn't doing the heavy lifting, but rather instantiating and running things which are doing the lifting.
2) Compose your functions so that they all receive all of the props that they require, to do their jobs, and moreso, they nearly always receive them from other files (not the file they're defined in), unless the assignment is just as a default value.
In the case of a controller, if you wanted the controller to be unit-testable, you would preconfigure it to accept a ProductModel to use (or a function to use) rather than assuming you are going to pull in the real model.
export const CreateProduct = (ProductModel) => async (req, res, next) => {
const productData = req.body
try {
const product = await ProductModel.create(productData)
res.send(product)
} catch (err) {
next(err)
}
}
import { CreateProduct } from './create_product'
import { ConfigureProductModel } from './somewhere_else'
export const ProductRouter = express.Router()
ProductRouter.post('/', CreateProduct(ConfigureProductModel(database)))
Now to test that, you can easily create a CreateProduct where you pass in a fake ProductModel. And if you use the example, there of ConfigureProductModel as a factory, you can test it by passing it a fake db instance (if you do, indeed have that level of control).
And personally, like I said, as a controller, I want to remove as much control as possible, so I'd probably take most of the imperative code away.
const CreateProduct = ProductModel => (req, res, next) =>
ProductModel.create(req.body)
.then(product => res.send(product))
.catch(next);

Abstracting the superagent

Our application consists of nodejs, express, reactjs, and newforms.
To make rest calls we are using :
var RestClient = require('superagent-ls')
And we are making rest calls like:
cleanBirthDate(callback) {
var {birthDate} = this.cleanedData
var formattedDob = moment (birthDate).format('DDMMYYYY')
RestClient.get(Global.getBirthDateServiceUrl() + '/' + formattedDob)
.end((err, res) => {
if (err) {
callback (err)
}
else if (res.clientError) {
var message = errorsMappingSwitch(res.body.error)
callback(null, forms.ValidationError(message))
}
else {
callback(null)
}
})
},
We want to move the RestClient related code to our own file say RestCleint.js and then require it and use it across the application. By doing so we can apply some generalised code(like error handling, logging, redirect to specific error pages depending on the error code) in one place.
Appreciate any help in this direction.
I did the exact same thing you require (even with using superagent). I created modules with the API code in a /utils folder and required them where applicable. For even more abstraction we're using CoffeeScript to create classes that inherit from a BaseAPIObject and invoke using something like API.Posts.getAll().end() etc.
This article was very helpful in understanding how to write your own modules: Export This: Interface Design Patterns for Node.js Modules.
you can always require it like
RestClient.js
export default function callApi(callback) {
//your rest code
// use the callback here in the callback of your call.
}
app.js
import {callApi} from './RestClient';
callApi((err, result) => {
if (err) console.log(err)
});

What is the correct way to "hook" into app.response functions in express?

I need to way to add some data to app.json and app.jsonp responses in express after app.json is called, what is the correct way to add middleware to this? I know I could probably do something like:
var jsonTemp = function(status, data) {
if (!data) {
data = status;
status = 200;
}
data.extraValue = 'foo';
res.json(status, data);
}
res.json = jsonTemp;
but monkey patching like that seems like a "bad idea". Is there an official way to hook into the response with some kind of middleware?
I think that monkey patching actually might be the best solution without knowing more about what problem you are trying to solve. The code you showed does however crash the server so don't use that.
app.use(function (req, res, next) {
var orig = res.json;
res.json = function json(status, data) {
if (!data) {
data = status;
status = 200;
}
data.extraValue = 'foo';
orig.call(this, status, data);
};
next();
});
Here I'm storing the original function in the variable orig and then delegating to that one when I'm done with the modification. Your code called your modified function over and over again, since that one now lives under res.json.
You can register your own template engine to format your output. Maybe that helps.

How to add two function as callback in node?

How can I callback two function in node?
For example:
request('http://...',[func1,func2])
Is there any module for that?
Is there some reason you can't put a wrapper around the two functions, then use the wrapper as a callback? For example:
function handleCallback(data)
{
func1(data);
func2(data);
}
request('http://...',handleCallback);
As noted in other answers it's trivial to wrap multiple functions in a single callback. However, the event emitter pattern is sometimes nicer for handling such cases. A example might be:
var http = require('http');
var req = http.request({ method: 'GET', host:... });
req.on('error', function (err) {
// Make sure you handle error events of event emitters. If you don't an error
// will throw an uncaught exception!
});
req.once('response', func1);
req.once('response', func2);
You can add as many listeners as you like. Of course, this assumes that what you want your callbacks to be registered on is an event emitter. Much of the time this is true.
You can create a little module for that:
jj.js file:
module.exports.series = function(tasks) {
return function(arg) {
console.log(tasks.length, 'le')
require('async').eachSeries(tasks, function(task, next) {
task.call(arg)
next();
})
}
}
example of use:
var ff = require('./ff.js')
function t(callback) {
callback('callback')
}
function a(b) {
console.log('a', b)
}
function b(b) {
console.log('b', b)
}
t(ff.series([a, b]))

Remove route mappings in NodeJS Express

I have a route mapped as:
app.get('/health/*', function(req, res){
res.send('1');
});
How can I remove / remap this route to an empty handler at runtime?
This removes app.use middlewares and/or app.VERB (get/post) routes. Tested on express#4.9.5
var routes = app._router.stack;
routes.forEach(removeMiddlewares);
function removeMiddlewares(route, i, routes) {
switch (route.handle.name) {
case 'yourMiddlewareFunctionName':
case 'yourRouteFunctionName':
routes.splice(i, 1);
}
if (route.route)
route.route.stack.forEach(removeMiddlewares);
}
Note that it requires that the middleware/route functions have names:
app.use(function yourMiddlewareFunctionName(req, res, next) {
... ^ named function
});
It won't work if the function is anonymous:
app.get('/path', function(req, res, next) {
... ^ anonymous function, won't work
});
Express (at least as of 3.0.5) keeps all of its routes in app.routes. From the documentation:
The app.routes object houses all of the routes defined mapped by the associated HTTP verb. This object may be used for introspection capabilities, for example Express uses this internally not only for routing but to provide default OPTIONS behaviour unless app.options() is used. Your application or framework may also remove routes by simply by removing them from this object.
Your app.routes should look similar to this:
{ get:
[ { path: '/health/*',
method: 'get',
callbacks: [Object],
keys: []}]
}
So, you should be able to loop through app.routes.get until you find what you are looking for, and then delete it.
The above approach requires you have a named function for the route. I wanted to do this as well but didn't have named functions for routes so I wrote an npm module that can remove routes by specifying the routing path.
Here you go:
https://www.npmjs.com/package/express-remove-route
It is possible to remove mounted handlers (added with app.use) while the server is running, although there is no API to do this, so it isn't recommended.
/* Monkey patch express to support removal of routes */
require('express').HTTPServer.prototype.unmount = function (route) {
for (var i = 0, len = this.stack.length; i < len; ++i) {
if (this.stack[i].route == route) {
this.stack.splice(i, 1);
return true;
};
}
return false;
}
This is something I need, so it's a shame there isn't a proper api, but express is just mimicing what connect does here.
app.get$ = function(route, callback){
var k, new_map;
// delete unwanted routes
for (k in app._router.map.get) {
if (app._router.map.get[k].path + "" === route + "") {
delete app._router.map.get[k];
}
}
// remove undefined elements
new_map = [];
for (k in app._router.map.get) {
if (typeof app._router.map.get[k] !== 'undefined') {
new_map.push(app._router.map.get[k]);
}
}
app._router.map.get = new_map;
// register route
app.get(route, callback);
};
app.get$(/awesome/, fn1);
app.get$(/awesome/, fn2);
And then when you go to http://...awesome fn2 will be called :)
Edit: fixed the code
Edit2: fixed again...
Edit3: Maybe simpler solution is to purge routes at some point and repopulate them:
// remove routes
delete app._router.map.get;
app._router.map.get = [];
// repopulate
app.get(/path/, function(req,res)
{
...
});
You can look into Express route middleware and possibly do a redirect.
As already mentioned above, the new Express API doesn't seem to support this.
Is it really necessary to completely remove the mapping? If all you need is to stop serving a route, you can easily just start returning some error from the handler.
The only (very odd) case where this wouldn't be good enough is if dynamic routes were added all the time, and you wanted to completely get rid of old ones to avoid accumulating too many...
If you want to remap it (either to do something else, or to map it to something that always returns an error), you can always add another level of indirection:
var healthHandler = function(req, res, next) {
// do something
};
app.get('/health/*', function(req, res, next) {
healthHandler(req, res, next);
});
// later somewhere:
healthHandler = function(req, res, next) {
// do something else
};
In my opinion this is nicer/safer than manipulating some undocumented internals in Express.
There is no official method but you can do this with stack.
function DeleteUserRouter(appName){
router.stack = router.stack.filter((route)=>{
if(route.route.path == `/${appName}`){
return false;
}
return true;
});
}
appName is path name .
Filter the methods in the router.route.stack where the router is express.Router
or you can do the same for the app but with app._router.stack.
Note: For below 4.0 use - app.router.stack.

Resources