I have an API in ExpressJS and a middleware that gets executed before each endpoint controller:
app.use(segregationMiddleware);
app.get('/some-endpoint', controller1);
app.get('/some-endpoint-2', controller2);
The segregationMiddleware is used to look for some parameters in the request and then it calculates a value that then is stored in the request object as req.locals.domain so the controllers can access it.
In each Mongoose model, I define a field named domain using a Mongoose plugin (so I don't have to do it every time). That field is used to segregate my assets. This means that when the segregationMiddleware populates req.locals.domain = 'foo' for example, if I make a model.find({}) I want to get only assets that have { domain: 'foo' }. Same thing if I try to update, save, delete, and so.
Of course, I can just simply modify the query on each controller since I have accesso to req, but I need to do it every time and I need to remember it for finds, findAndUpdate, save, and soo... sooner or later I'm gonna forget it.
I can define some hooks in Mongoose that will modify the query using a plugin so it adds the domain constraint to the query so I don't have to do it in the controller BUT I don't have the current req object in the Mongoose plugin unless I pass it, and the only way that come to my mind is to abstract the DB methods in the plugin, so in the controller, I do something like this:
model.safeFind(req, query);
And in the plugin I define safeFind like:
safeFind = () => {
const theRealQuery = Object.assign({}, query, { domain: req.locals.domain });
return this.find(query);
}
BUT, in this way, I need to redefine each DB query function (find, findOne, update, save...) and I need to remember to use the safe methods. Then again, I'm going to forget it sooner or later.
Is there a way I can still use the regular Mongoose methods from the controller and have the plugin somehow modify the query for every method using the current req object?
Related
I was asked to make API call to send data.
On Click in vue, I was firing this event
async facebookDataToSend () {
let campaignID = await this.$store.getters['CurrentInstance/id']
this.$axios.post(process.env.API_BASE_URL + 'faceeBookCampaign', { campaignID: campaignID }, { withCredentials: true })
},
But then, I was told to use API functions which already exsist in some xyz.js file.
My xyz.js file looks like this..
const rest = {
something: axios.create({
baseURL: process.env.API_BASE_URL,
withCredentials: true
}),
setClient: function (client) {
this.something = axios.create({
baseURL: process.env.API_BASE_URL,
withCredentials: true,
params: {
__somethingClient: client
}
})
this.client = client
}
}
Here, I am unable to comprehend how can I use this instance to make an api call So I viewed the code where they have already made the api call and saw something like this
const API = {
url: '/whateverHtml/',
method: 'post',
withCredentials: true,
data: {
'schemaType': 'something-event', // TODO FIXME
'field': 'description', // TODO FIXME
'html': this.model[this.field.key]
}
api.something.request(API).then(result => {
And I wasn't able to comprehend the code. For starters
What is request? I don't see my any method or property inside something in my rest variable
second why are they using withCredentials: true in their API object when they have already set up the property as true in rest object]
What are the pro's of using axios.create({ i.e what they are doing than what I initially did this.$axios.post(
request is a method defined by axios. Link to docs.
request allows you to make an HTTP call with any verb you want (POST, GET, DELETE, PUT). Most likely axios calls request from inside all the other helper methods (get, post), but this is an implementation details. One of the advantages of using request is that you don't have to hardcode the HTTP verb (POST, GET ...) and you can set it at run time depending on your input.
I see 2 reasons why they would set withCredentials:
setClient may or may not be called before something
for clarity: it's enough to look at the definition of something to realise that the client is using credentials and you don't need any extra information about how rest works.
I don't think the request for you to use something boils down to advantages of axios.$post vs axios.create. It's probably related more to how to organise your code.
Some advantages of using a separate module vs calling axios directly
when calling axios directly you are prepending base url all the time, when using a module for your REST API the base URL is tucked away and arguably makes your code easier to read
you can bake other options inside config and make sure they are used. For instance you may have an access token, the module can store that token and always added to any request. When calling axios by hand you need to remember this
you are decoupled from axios (to some degree)(1). When using a module you don't actually care if it's axios doing the requests or not.
You can add more API calls to the module that you can reuse in the future. I'm expecting xyz file to grow in time and your call to faceeBookCampaign to end up being a method on the rest variable. It makes more sense to end up using this.client and not something but this is up to the devs.
it keeps all the REST API calls in one place allowing you to build an SDK for that API, which as the project grows can have its own life cycle.
(1) I say that id decouples you to some degree because there are semantics that need to be kept so everything works. The returned object needs to have a request method that accepts a config object. The config need to conform to the same structure as the one that axios wants. But, you can always write an adapter for that, so you are actually decoupled from axios.
request here takes a config and returns a promise. I am guessing this approach is usually taken when you want to reuse a request object that is created using create (at least my sense).
I feel the request method is used to overwrite the initial configuration with new one defined in API. And, the double withCredentials should be an oversight.
OR, because API is defining a new config object, therefore when defined without withCredentials, it would overwrite the create's configuration.
Hence, it looks like its specified twice.
I'm trying to load includes on an existing model in sequelize. In express we pre check the models to see if they exist in the middleware.
So once we're in the actual "controller" we want to run some includes on that existing model that is passed in.
req.models.item.incude([
{model: Post, as: 'posts'}
])
Is there any way to accomplish this?
EDIT:
I know we can do something like this.
return req.models.item.getThing()
.then(function (thing) {
req.models.item.thing = thing;
return req.models.item;
});
But:
My expansions for includes are a dynamic property that come via url parameters, so they are not know ahead of time.
It I return the above you will not see the "thing" in the response. I need it nicely built as part of the original instance.
Something like a .with('thing', 'other.thing'); notation would be nice. Or in the case of sequelize .with({include: ...}); or .include([{model: ...}]);
If the variable req.models.item is already an Instance but without its other related instances ("includes"), then you could include them using something like the following code:
Item.findAll({
where: req.models.item.where(),
include: [{
model: SomeAssociateModel,
}]
})
.then(function(itemWithAssoc) {
// itemWithAssoc is an Instance for the same DB record as item, but with its associations
});
See here for some documentation. See here for a script demo'ing this.
Update: Given the instance, how do I just get the associated models?
To do this just use the automatically generated "getAssociation" getter functions, e.g.:
function find_associations_of_instance(instance) {
return instance.getDetails();
}
I've updated the script to include this as an example. For more information on these functions, see the SequelizeJS docs.
I'm using toJSON() method of my model in Sails in order to control the visibility of some of it's properties, when model is exposed via application's API.
In order to decide which properties to display and which to omit I need to know the permissions of the current user. So, how do I get the current user from inside the model? Or is there a better way (pattern) to solve this problem?
Here's some sample code I want to achieve:
toJSON: function () {
var result = {};
result.firstName = this.firstName;
result.lastName = this.lastName;
// Exposing emails only to admin users.
if (currentUser.isAdmin()) {
result.email = this.email;
}
return result;
}
Your asking about reading a session inside the model call. Currently the way sails and waterline are built you can not do this.
You can use the select property on your initial model call to restrict the columns returned. Since this would be in the context of your controller you would have access to the req object.
Here are a bunch of related questions / answers on this topic.
sails.js Use session param in model
Is it possible to access a session variable directly in a Model in SailsJS
https://github.com/balderdashy/waterline/issues/556
https://github.com/balderdashy/waterline/pull/787
Sails Google Group Discussion on the topic
Let's say we create a sails.js model, which sometimes should be saved into DB when posted (as usual), and sometimes – should not. Can we perform this logic in a model lifecycle callback?
Basically, it gives us just two ways – proceed as usual calling next() or raise an error calling next(err). Are there any other options? Maybe it's somehow possible to get access to req/res objects from inside the callback?
module.exports = {
attributes: {
},
// Lifecycle Callbacks
beforeCreate: function(values, next) {
//analyze values
if (someCondition) {
//now we realize that we don't want the model to be created
//we need perform some other stuff and respond with some custom answer
//how do we do that?
} else {
next();
}
}
};
So that's the job of the Controller, you don't want to bring in req/res objects to the model. The check to see if a record should be created or not belongs in a controller method. By the time Model.create() is called you should already know if you want to create it or not. If you want to use the Blueprints or reduce code repetition you can use a policy (middleware) that you can attach to routes and do the check before the Model.create() is called.
I want to override backbone.sync i have already asked this but the problem is i don't quite get it. I need to know where to put the codes if i were to override the sync function.
If i put it on the model like this
model = Backbone.Model.extend({ sync:"" });
Then how should i call it? if i were to use the save method. Also i need to change the methodMap of create from POST to PUT. temporarily i resorted to this 'create': 'PUT', actually editing the backbone.js file ( iknow its not good ). Before i forgot i also need to add this
sendAuthentication = function (xhr) {
xhr.setRequestHeader('Authorization', auth)
};
As a beforeSend parameter since my server has authentication. Again where should i do it? Where should i go and put the codes? in my model? in my collection? or in my views? Any help? THank you.
update
Also can i override the sync on my collection? i mean can i have something like this?
collection = Backbone.Collection.extend({ sync:""});
The strategy behind Backbone framework is to make it simple for editing and flexible for every need. So if you look up the source code you'll find out that every method, which calls Backbone.sync in fact calls first "this.sync".
From the Backbone manual you can read :
The sync function may be overriden globally as Backbone.sync, or at a
finer-grained level, by adding a sync function to a Backbone
collection or to an individual model.
So you have two options
Option One - Replacing global Backbone.sync function
If you override the global Backbone.sync you should place your code in your global application file ( actually anywhere you want, but it must be evaluated ( executed ) at your initial javascript loading, to work as expected
// Anywhere you want
Backbone.sync = function(method, collection, options) {
console.log(method, collection options)
}
This will override Backbone.sync and actually will display on your console what is called every time you call collection.fetch, save, delete, etc.
Here you have no default Methodmap, infact you have nothing else except the arguments :
method - which is a string - 'read', 'create', 'delete', 'update'
collection - which is your collection instance which calls the method
options - which has some success, error functions, which you may or may not preserve.
Debug this in your browser, while reading the Backbone source code, it's very easy to understand.
Option Two - Adding to your model/collection sync method
This is used if you wish to use the default Backbone.sync method for every other model/collection, except the one you specifically define :
mySocketModel = Backbone.Model.extend({
sync : function(method, collection, options) {
console.log('socket collection '+this.name+' sync called');
}
});
Partners = new mySocketModel({ name : 'partners' });
Users = new mySocketModel({ name : 'users' });
Log = new Backbone.Collection;
So if you call Partners.fetch() or Users.fetch(), they won't call Backbone.sync anymore, but yor Log.fetch() method will.