express middleware to consistently format response - node.js

I'm trying to decide on a consistent response JSON structure and I found this SO answer.
I like the simplicity of JSend but now I'm wondering what is a clean way to implement that structure in express without having to manually create it each response or use a constructor in every controller method to help build the structure. I found jsend-express npm but it has so few downloads I'm worried about relying on it.
Can anyone recommend some ways to automatically enforce some of this structure in express myself?
However, I'm not sure why a status key is even necessary when the 3 states seem to already be covered by HTTP statuses and I don't see this key recommended in google's style guide, so that's another reason I may not want to use the jsend package and just do it myself to omit some keys and add later if needed.

This ended up being how I did it based on this SO answer.
I have a catch all error handler that adds the error key, but for all non-errors I wanted the data to be wrapped in a data key without having do it in every controller method by hand, or to call another function in every controller method before the res.json since that is also repetitive.
/**
* Nests all successful res data in a `data` key. Any additional meta-data
* that needs to be present at the top level json object can be added here.
* #param {request} _req
* #param {response} res
*/
const modifyResponseBody = (_req, res, next) => {
const resDotJson = res.json;
res.json = function (data) {
const isError = data?.hasOwnProperty?.("error") === true;
if (!isError) {
arguments[0] = { data: data };
}
resDotJson.apply(res, arguments);
};
next();
};
app.use(modifyResponseBody);

Related

Accessing variable in parent scope

I have a custom logging function which is assigned to express requests req.log object using middleware.
The purpose of this is for the logging funtion to be able to read the request headers.traceid, before transmitting the log event to a seperate service.
This is working perfectly fine using middlware or with an extra parameter in the function, however to simplify the use of it.
What I'd really like to know if there's a way for the function to be able to read the req object from the scope it was called in, without referencing it either using middlware or as a function paramter?
// log.js
module.exports = () => {
console.log(...arguments)
const req = getVarFromParentScope("req") || undefined
const traceId = req?.headers.traceid || null
// POST { timestamp, traceId, args: { ...arguments } } to logging service
}
No, it isn't.
(And if it was, then the ability for a function to access a variable inside another function just because it was the calling function would make it very easy to write code that was very hard to debug.)

Call Express router manually

Нello! I am looking to call a function which has been passed to an expressRouter.post(...) call.
This expressRouter.post(...) call is occurring in a file which I am unable to modify. The code has already been distributed to many clients and there is no procedure for me to modify their versions of the file. While I have no ability to update this file for remote clients, other developers are able to. I therefore face the issue of this POST endpoint's behaviour changing in the future.
I am also dealing with performance concerns. This POST endpoint expects req.body to be a parsed JSON object, and that JSON object can be excessively large.
My goal is to write a GET endpoint which internally activates this POST endpoint. The GET endpoint will need to call the POST endpoint with a very large JSON value, which has had URL query params inserted into it. The GET's functionality should always mirror the POST's functionality, including if the POST's functionality is updated in the future. For this reason I cannot copy/paste the POST's logic. Note also that the JSON format will never change.
I understand that the issue of calling an expressjs endpoint internally has conventionally been solved by either 1) extracting the router function into an accessible scope, or 2) generating an HTTP request to localhost.
Unfortunately in my case neither of these options are viable:
I can't move the function into an accessible scope as I can't modify the source, nor can I copy-paste the function as the original version may change
Avoiding the HTTP request is a high priority due to performance considerations. The HTTP request will require serializing+deserializing an excessively large JSON body, re-visiting a number of authentication middlewares (which require waiting for further HTTP requests + database queries to complete), etc
Here is my (contrived) POST endpoint:
expressRouter.post('/my/post/endpoint', (req, res) => {
if (!req.body.hasOwnProperty('val'))
return res.status(400).send('Missing "val"');
return res.status(200).send(`Your val: ${req.body.val}`);
});
If I make a POST request to localhost:<port>/my/post/endpoint I get the expected error or response based on whether I included "val" in the JSON body.
Now, I want to have exactly the same functionality available, but via GET, and with "val" supplied in the URL instead of in any JSON body. I have attempted the following:
expressRouter.get('/my/get/endpoint/:val', (req, res) => {
// Make it seem as if "val" occurred inside the JSON body
let fakeReq = {
body: {
val: req.params.val
}
};
// Now call the POST endpoint
// Pass the fake request, and the real response
// This should enable the POST endpoint to write data to the
// response, and it will seem like THIS endpoint wrote to the
// response.
manuallyCallExpressEndpoint(expressRouter, 'POST', '/my/post/endpoint', fakeReq, res);
});
Unfortunately I don't know how to implement manuallyCallExpressEndpoint.
Is there a solution to this problem which excludes both extracting the function into an accessible scope, and generating an HTTP request?
This seems possible, but it may make more sense to modify req and pass it, rather than create a whole new fakeReq object. The thing which enables this looks to be the router.handle(req, res, next) function. I'm not sure this is the smartest way to go about this, but it will certainly avoid the large overhead of a separate http request!
app.get('/my/get/endpoint/:val', (req, res) => {
// Modify `req`, don't create a whole new `fakeReq`
req.body = {
val: req.params.val
};
manuallyCallExpressEndpoint(app, 'POST', '/my/post/endpoint', req, res);
});
let manuallyCallExpressEndpoint = (router, method, url, req, res) => {
req.method = method;
req.url = url;
router.handle(req, res, () => {});
};
How about a simple middleware?
function checkVal(req, res, next) {
const val = req.params.val || req.body.val
if (!val) {
return res.status(400).send('Missing "val"');
}
return res.status(200).send(`Your val: ${val}`);
}
app.get('/my/get/endpoint/:val', checkVal)
app.post('/my/post/endpoint', checkVal)
This code isn't tested but gives you rough idea on how you can have the same code run in both places.
The checkVal function serves as a Express handler, with request, response and next. It checks for params first then the body.

Differentiate between nodejs request and response

There is a generic structure of nodejs callback functions :
function(req,res){
//handle callback
}
I just want, callback should work correctly even if sometimes i write in mistake (res, req)
Given mixture of req or res, how do i find which one is actually request and which one is response.
req is an IncomingMessage object and res is a ServerResponse object.
So check for unique properties on each, for example if the particular object has a writeHead() function, then it's the response object.
You may also be able to use instanceof to check: res instanceof http.ServerResponse.
Functions in JavaScript are not programmatically prototyped by parameter names. The length property of a function only provides the number of parameters specified in the definition:
var fn = function (one,two,three) { return "x"; };
console.log(fn.length); // 3
Although there are ways to retrieve these names (see this question), usually procedures simply ignore how you name the parameters of your functions/closures, and instead assume that you are following the proposed API.
For this reason, it remains as the best practice to pay attention to the API and name parameters accordingly. In a Node.js HTTP request listener, the request comes always before the response (it is documented and many examples are available). As mentioned by other answers, you can dynamically check whether the request is an http.IncomingMessage or whether the response is an http.ServerResponse, but it seems to me that you can avoid introducing an overhead just with proper naming.
With that said, given the variables req and res, it is easy to make a check at the top of a function body, like the code below. However, do note that this would only be remedying what can be prevented by just following the API contracts, and as thus I cannot recommend it (unless you really want to make functions with a more flexible API).
function(res,req) {
if (req instanceof http.ServerResponse) {
// wrong order, swap.
var t = req;
req = res;
res = t;
}
// handle request
}

How to identify request (by ID) through middleware chain in Express.

I am developping a RESTful server in node.js, using Express as framework, and Winston, for the moment, as logger module.
This server will handle a big amount of simultaneous request, and it would be very useful to me to be able to track the log entries for each specific request, using something like a 'request ID'. The straight solution is just to add this ID as another piece of logging information each time I want to make a log entry, but it will mean to pass the 'request ID' to each method used by the server.
I would like to know if there is any node.js/javascript module or technique that would allow me to do this in an easier way, without carrying around the request ID for each specific request.
If you auto-increment, your later log analytics won't be able to uniquely identify requests, because different instances will generate colliding IDs, and restarting the app will automatically cause ID collisions.
Here's another possible solution.
Install cuid:
npm install --save cuid
Then in your main app file:
var cuid = require('cuid');
var requestId = function requestId(req, res, next) {
req.requestId = cuid();
next();
};
// Then, at the top of your middleware:
app.use(requestId);
Now you'll get a friendly request ID that is unlikely to collide, and you'll be able to uniquely identify your requests for your log analytics and debugging, even across multiple instances, and server restarts.
You can use req object that does comes with every request in express.
So the first route you would do in your application would be:
var logIdIterator = 0;
app.all('*', function(req, res, next) {
req.log = {
id: ++logIdIterator
}
return next();
});
And then anywhere within express, you can access that id in req object: req.log.id;
You will still need to pass some data into functions that do want to create some logs. In fact you might have logging function within req.log object, so that way it will be guaranteed that logging will happen only when there is access to req.log object.
I was struggling search for a solution for this problem.
The thing I didn't like it about solutions suggested here was that they imply to share the req object among all the functions along the project.
I found out a solution mixing your approach (creating an uuid per request) and with a library (continuation-local-storage) that allows sharing namespaces among modules.
You can find the explanation in this other answer: https://stackoverflow.com/a/47261545/5710581
If you want more info, I wrote down all these ideas and all the code in a post, in order to explain everything in one place:
Express.js: Logging info with global unique request ID – Node.js
You shouldn't be using Global Variables.
What I like to do is to populate a META object before each request.
I use a UUID generator (https://github.com/kelektiv/node-uuid) to ID a request
Here's an example
app.all('*', function(req, res, next) {
req.meta = {
ip: req.headers['x-forwarded-for'] || req.connection.remoteAddress,
timestamp: uuid(),
user_agent: req.headers['user-agent'],
body: req.body,
}
return next();
})
As mentioned by #moka , Using the request ID in each request is the crux of solving the problem. Another way of abstracting all these is by making use of http-context and uuid
So set a UUID in the httpContext before all your middlewares (set as an application middleware and not as a router middlware). now you can get the uuid anywhere in your code and log it.
Here is a sample implementation I have used
You can get the complete reference here uuid in request
const uuid = require('node-uuid');
const httpContext = require('express-http-context');
....
this.expressApp.use(httpContext.middleware);
this.expressApp.use((req, res, next) => {
httpContext.set('reqId', uuid.v4());
next();
});
Now I have used the reqId set here in my custom pino logger'
public infoLogService (fileName): pino.Logger {
return pino({
level: 'info',
name: this.appService.getApp_name(),
messageKey: 'XXX-Logs',
base: {pid: process.pid, hostname: os.hostname,
timestamp: this.getTimeStamp(),
appName: this.appService.getApp_name(),
fileName: fileName,
request_id: **isNullOrUndefined(httpContext.get('reqId'))** ? 'Not an actual request ' : httpContext.get('reqId')
},
enabled: true,
useLevelLabels: true,
});
}
If the reqId is null it means that the loggers have been inserted in code that is used before starting the express App. Hope you can use this as an alternate solution

Backbone.js/express.js parameters for model.save()

I'm using Backbone.js on the client and node.js on the backend, and I'm having a bit of trouble doing a 'limited' model save, as explained here : http://backbonejs.org/#Model-save
As in the example, if I do
book.save({author: "Teddy"});
how do I access the parameters of the save using express.js, i.e. the fact that I only want to save to the 'author' field? I've tried the following
req.body -> gives ALL parameters of the model being saved, I want only the 'author' field
req.query -> empty
Any help much appreciated!
As stated in Model.save documentation:
save model.save([attributes], [options])
[...] The attributes hash (as in set) should contain the attributes you'd like to change —
keys that aren't mentioned won't be altered — but, a complete
representation of the resource will be sent to the server.
You can however override the save method and provide a data attribute via the options which will be sent to the server instead of the full model representation. For example, this will only save the attributes passed to the method :
var M = Backbone.Model.extend({
save: function (attrs, options) {
options || (options = {});
options.contentType = 'application/json';
options.data = JSON.stringify(attrs);
Backbone.Model.prototype.save.call(this, attrs, options);
}
});
And a Fiddle http://jsfiddle.net/dLFgD/
As #mikebridge noted in the comments, this behavior can now be obtained by passing an attrs option. So either use
book.save(null, {
attrs: {author: "Teddy"}
});
or keep the override
var M = Backbone.Model.extend({
url: '/echo/json/',
save: function(attrs, options) {
options || (options = {});
options.attrs = attrs;
Backbone.Model.prototype.save.call(this, attrs, options);
}
});
http://jsfiddle.net/nikoshr/dLFgD/7/
You could also send a PATCH request if you're using a Backbone version that supports it (>=0.9.9) and your server understands that verb, as explained in #pkyeck's answer
with the current version of Backbone (1.1.0) you could also do a PATCH which only sends the changed attributes to the server.
If instead, you'd only like the changed attributes to be sent to the server, call model.save(attrs, {patch: true}). You'll get an HTTP PATCH request to the server with just the passed-in attributes.
taken from here: http://backbonejs.org/#Model-save
in your case this would be:
book.save("author", "Teddy", {patch: true});
The best way I have found to access POST parameters is to use the bodyParser middleware. This is referenced at this question: How to retrieve POST query parameters?
/* On startup, register the bodyParser middleware */
app.use(express.bodyParser());
/* Then, during route execution, each parameter can be accessed with req.param */
app.post('/author/:id', function(req, res) {
var author = req.param('author', null); // Second parameter is default.
...
});

Resources