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

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

Related

express middleware to consistently format response

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

node.js request post retrieve last id from db.json

After posting in db.json a new record (transaction with several data), I would like to get the id of the new record before going on. How-to do it ? I am stuck
I have in my db.json a "table" positions with an id automatically generated
my request is
request({
url: url + '/api/positions,
method: 'POST',
form: transaction
}, ????);
I want in my callback retrieve position.id (as I have to use it in another part of code).
Thanks a lot
Olivier
Make sure your API is giving back the position id, Im assuming you're using express, so in /api/positions have the response be res.send({position...});
EDIT, So you're asking about routing? Here is an awesome guide: https://expressjs.com/en/guide/routing.html but Ill give you an example of what I mean.
app.post('/api/positions', function(req, res) {
//your logic goes here, so you are sending a 'transaction' object
// npm install and use body parser so you can retrieve attributes
// from the body of the post
res.send({position: x})
})
Maybe some thing like that.
...
var router = jsonServer.router('db.json');
...
// 'router.db.get' will fetch any first level attribute from your db.json
var positions = router.db.get('positions').value();
var lastPosition = positions[positions.length-1];

Sails JS: Pass parameter to toJSON

The sailsjs model attribute method toJSON is very handy for processing model attributes before sending back to client. However, this method does not take any parameters and I cannot pass additional information to the method that can be used for processing attributes.
In my particular case, I need to check if I should send an attribute by comparing logged in user id (req.user.id). E.g.
toJSON: function () {
var obj = this.toObject();
var result = {};
if (req.user.id === 123) { // How can I access req from inside toJSON?
// do something here ...
}
}
I could not find a way to access the req parameter from inside toJSON. Any suggestions?
Sails does purposely not expose the req attribute as a global so there is no way to access it from within toJSON:
How to access session variables outside controller
Depending on the functionality you are trying to achieve you might be able to use policies instead: How to access request object in sails js lifecycle callbacks
In a similar case, I found myself using a workaround instead. You can add the id of the logged in user in your controller to the model:
YourModel.findOne().exec(function (err, yourModelObject) {
yourModelObject.loggedInUserId = req.user.id;
res.send(yourModelObject);
});
It is then accessible in your toJSON method and you could use it like this:
toJSON: function () {
var obj = this.toObject();
var result = {};
if (obj.loggedInUserId === 123) {
// do something here ...
}
delete obj.loggedInUserId;
return obj;
}
It is a little bit of a hack, but it worked for me. Be careful that a user can't somehow set the same value on the model (for example by using schema: true in your model) in question, to prevent security holes in your code.

How to save Backbone JSONP model to MongoDB using Node/Express?

I need a way to parse my JSONP object on server side to save it, due to cross domain origin issue I have shifted my way of communication from JSON to JSONP but not finding any suitable way to parse JSONP on server side to save it to the database.
Following is the Model,
define(['backbone'],function(Backbone){
'use strict';
return Backbone.Model.extend({
url:"http://crossdomain:9847/page",
defaults: {
type:'text',
position:0,
align:'left',
text:{"en":""},
color:"#000",
weight:'normal',
size:"14px",
font:"Verdana",
pageid:'askdkasdkgaskdgks'
},
idAttribute:'_id',
sync: function(method, collections, options) {
options.dataType = "jsonp";
return Backbone.sync(method, collections, options);
}
});
});
Express Server,
var express = require('/root/node_modules/express');
var page = require('./routes/page.js');
var app = express();
app.configure(function () {
app.use(express.json());
app.use(express.urlencoded());
app.set("jsonp callback", true);
})
app.get('/page', page.updatePage);
app.listen(9847);
exports.updatePage = function(req, res) {
console.log(req.query);
// Here how I can parse the req is my problem
// so I can save object to database?
}
URL is generating like,
http://crossdomain:9847/page?callback=jQuery203010156283635587138_1384408493698&{%22text%22:{%22en%22:%22Text%22},%22type%22:%22text%22,%22position%22:0,%22align%22:%22left%22,%22color%22:%22#000%22,%22weight%22:%22normal%22,%22size%22:%2214px%22,%22font%22:%22Verdana%22,%22pageid%22:%22askdkasdkgaskdgks%22}&_=1384408493700
and I am able to receive,
{ callback: 'jQuery203010156283635587138_1384408493698',
'{"text":{"en":"Text"},"type":"text","position":0,"align":"left","color":"': '' }
Now how can I parse this ? I can get callback from callback parameter, but how to get actual data ?
You can't parse the result because it's not valid JSON. Your problem is probably in this line:
app.set("jsonp callback", true);
This is where you set the JSONP callback variable, for example changing it from the default of callback to instead be callbackVariable.
Just comment out that line, and the JSONP you get back will hopefully be parseable. Or, you might also have to fix how Backbone is constructing the JSONP URL. If you instead used a URL like http://crossdomain:9847/page?callback=jQuery203010156283635587138_1384408493698&type=text&position=0&align=left&color=%23000&weight=normal&size=14px&font=Verdana&pageid=askdkasdkgaskdgks I believe it would work. Backbone seems to be adding additional encoding into the values in the URL, which makes parsing harder.
Finally, if you need help easily picking specific values out of a (valid) JSON string that has been parsed into a Javascript object, take a look at the many useful function in lodash.

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

Resources