Best practice to change default blueprint actions in sails.js - javascript

I'm looking for an answer to what is the best practice to customize default sails.js CRUD blueprints. The simple example of what I mean is let's say I need to create SomeModel, using standard blueprint POST \someModel action, and also I want to get information about authenticated user from req object and set this property to req.body to use values.user.id in beforeCreate function:
module.exports = {
attributes: {
// some attributes
},
beforeCreate: function (values, cb) {
// do something with values.user.id
}
};
I'm very new to sails.js and don't want to use anti-patterns, so I'm asking for inputs on what is the right way to handle such cases. Also it would be great to have some good resources on that topic.

There are a few options open to you which you've identified, and as usual, it depends on your use case. Here are some thoughts on the three options you listed in your comment:
Override controller/create - Of the three, this is the best option because it doesn't clog up code in routes or policies for a single instance.
Write controller/action and add to config/routes.js - While this works, it defeats the purpose of using blueprints, and you have to do all the code in option 1 plus making your routes code messier.
Apply a policy for a single create action - To do this, you will not only have to clutter up your /policies folder but also your policies.js file. This is more code AND it puts the logic for one controller method in two separate places, neither of which is the controller.
Out of these three, option 1 is the best because it contains the code to its context (the controller) and minimizes written code. This is assuming that you only want to alter the create method for one model.
Side note:
As an example of how your use case determines your implementation, here is how I've set my Sails app up which has a few REST routes, each of which responds differently based on the user calling the route (with a valid Facebook token):
Only uses blueprint action routes (not blueprint REST routes)
First goes through the policies:
'*': ['hasFBToken', 'isTokenForMyApp', 'extractFBUser', 'extractMyAppUser'] which store fbuser, myappuser, and other variables in req.body
At this point, my controller function (e.g. controller.action()) is called and can access information about that user and determine if it has the correct permissions to do CRUD.
Pros of this system:
All code for each CRUD operation is contained within its controller function
Minimal to no code in routes.js (no code) or policies.js (one line) or /policies
Works well with API Versioning (e.g. controllers/v1.0/controller.js), which is much easier to do with versioned controllers than versioned models. That means that I can create a new API version and simply by creating a controller in /v2.0 with a function action(), calls such as POST /v2.0/controller/action will exist with no extra routing needed.
Hopefully this example helps illustrate how design decisions were made to offer functionality like API versioning and consolidate code in its specific context.

Related

Design pattern to choose different database

I have created a class to interact with my database:
class MyDatabase {
connect() { /* ... */ }
}
And the database has two types of modes: admin and client. I would like to have different classes for each mode:
class MyDatabaseAdmin {}
class MyDatabaseClient {}
Both of them will implement the same interface, but with different underline implementations. I would like to know if there is a design pattern to instantiate the parent MyDatabase choosing one over the other. Something like:
const db = new MyDatabase({ mode: 'admin' })
db.connect() // parent calls MyDatabaseAdmin.connect
I don't need a perfect answer, just some direction to what I should search for.
Thanks.
If they have no code in common, then they are really just two separate classes. You probably don't even want the admin code to be present in the browser since it can't be used there anyway.
You could have a single factory function that looks at an admin parameter and instantiates the appropriate database object and returns it, but I don't think I even see why you'd bother with that.
Just have each client instantiate the appropriate DB object. If there's any common code, you can have both implementations inherit from a common base and put the common code in the base class.
Right! I forgot to mention, but there are some common code... the connection and authentication is exactly the same, for example. Do you happen to know any design pattern that would perfect fit for this use case? Should I research about Factories?
I think a common base class with inheritance solves the problem here. It gives you a natural place to put the common code without having to include server-side code in the client implementation. The browser implementation and the server implementation would each be a subclass that are present only on their respective platforms. The common base class would be present and used in both places.
You just have the client instantiate the appropriate leaf class and the shared code lives in the base class. If you want to hide the actual class constructors, you can use a factory function (it's just a simple function that looks at conditions and makes an appropriate object for you and returns it), but I'm not sure there's any compelling reason to use a factory function here. You would only include the appropriate leaf class on the platform for which it is intended so there's no place where you might use one or the other in the same code.
would you consider having only 'users'? then each user could have a 'role' that would be either 'user' or 'admin'. So based on that 'role' you will define the privileges for each user. I find this solution easier for this kind of stuff but let me know if it does not match your case.

Create a Javascript API Class with multiple base urls

My backend includes multiple microservices, each with its own base url. At the moment I have the user and the metadata services, but this could expand in the future.
I have a React app and I'm trying to create an API wrapper class to call when I need to modify something. My first approach was to create multiple api instances for each service and import as needed:
import userApi from '../userApi'
import metadataApi from '../metadataApi'
userApi.getUser(user_id)
metadataApi.getCollections()
But I'd like to use a different approach that wouldn't require keeping note where each entity is located in order to use it, like so:
import API from '../api'
API.getUser(user_id)
API.getCollections()
API.deleteUser(user_id)
But I'm not sure how I can achive this without bloating up the API class. Can I import an array of methods inside it and just attach them to the class prototype before exporting?
I want to find a suitable structure to better separate each entity and make it easier to build and modify it in the future.
To be honest, separating your API classes into separate files / modules is fine. It feels like a bit of an overhead when the app is small, but as it grows, it helps keeps things organised.
You have already indicated that your backend API's are structured into microservices, why not keep them separate entities in the front end too? It will be easier to manage your API classes when / if you ever come to start hitting different endpoints.
I have though, in the past, created a base class that each of those API classes may inherit from, where I can set up common logic, such as request headers etc, if you want to get some reuse that way.
I have even went a step further again which would create another level of abstraction that handles how the integration is happening, i.e. via HTTP, where I would declare which HTTP client to use for example. That way, if I ever change the HTTP client, I only change it in one place
That kind of structure looked like ->
_ServiceProxy.js
Common functions such as GET, POST, PUT, DELETE, etc.
HTTP client defined here
High level error handling defined here
_someBaseAPI.js
An an abstract client that would define how to interact with a set of common microservices, e.g. Auth logic etc
UserAPI.js
A concrete / static class, only interested in how to handle requests / responses to do with Users
You can define and export a separate component in which all api files will imported and use individual api in its functions, then you will be able to use its function for specific api.

How do I run a callback function after a SailsJS REST Blueprint?

Is there a best practice for a callback after a blueprint route in SailsJS 11.x?
I have an API where Users can submit Ideas. After the Idea is created, if it's the User's first Idea they should get a Badge. As far as I can tell there are two places to put this functionality.
Copy/Override the Sails rest blueprint, add the functionality to the Idea.create() callback function.
Add a lifecycle callback for the afterCreate action on the Idea model.
I'm assuming any functionality I write in terms of granting badges should be put in a Service so it can be shared among the different models. It seems like option 1 wouldn't be as tightly coupled, but would lead to fatter controllers.
Overriding the "create" blueprint will mean that every model will use that code when handling a request to their POST /<modelName> route, which is probably not what you want. The lifecycle callback will work fine for granting the badge, but if you're looking to modify the response based on whether or not a badge was granted, this approach won't work--but you could easily send a socket notification from the lifecycle callback, using User.message().
The third option is just to override the create action in IdeaController.js. There's not all that much to it, especially if you let Waterline handle most of the field validation for you. In essence, it's just:
create: function(req, res) {
Idea.create(req.params.all()).exec(function(err, idea) {
if (err) {return res.negotiate(err);}
// ... do some stuff with the idea and send a response ...
});
}
I would go for the second option, however you should note that afterCreate gets triggered when testing via barrels and adding items, which might be an issue for the logic in afterCreate. (Ex: A user has 3 ideas, 1 badge from the first idea, you add them to barrels fixtures. When barrels is adding the first idea it will trigger the afterCreate then it will add the badge from the fixture resulting in the user having 2 duplicate badges.)

How should I make configurable modules in AngularJS

I've been tinkering with AngularJS and I've built up a small collection of directives and services that I would like to package into a single JS file so that I can use them anywhere.
I have some website specific settings that my module will need for API calls and that sort of thing. I'm just wondering what the Angular way of making configurable modules is. Obviously I don't want to have to modify my reusable JS file for each website, as that kind of defeats the purpose of having it. Seeing as the values are going to remain the same for each website it seems like an awful lot of hassle to pass them in as an argument on each function call, and I'd rather stay away from global variables as much as possible.
I've searched a lot of questions for the answers I seek, and the closest pattern I've found so far is to have my reusable module be dependant on a not included module called "settings" or something and then define that module in the page's JS file, allowing the reusable module to pull the values from it. Here's an example to show what I mean.
This seems backwards to me. It's kind of like having a function pull values from global values instead of passing the values in as arguments.
Is this really the best way of doing this, or is there an alternative?
It sounds like you're looking for a provider.
You should use the Provider recipe only when you want to expose an API for application-wide configuration that must be made before the application starts. This is usually interesting only for reusable services whose behavior might need to vary slightly between applications.
Here's a very basic example of a provider:
myMod.provider('greeting', function() {
var text = 'Hello, ';
this.setText = function(value) {
text = value;
};
this.$get = function() {
return function(name) {
alert(text + name);
};
};
});
This creates a new service, just like you might with myMod.service or myMod.factory, but provides an additional API that is available at config time—namely, a setText method. You can get access to the provider in config blocks:
myMod.config(function(greetingProvider) {
greetingProvider.setText("Howdy there, ");
});
Now, when we inject the greeting service, Angular will call the provider's $get method (injecting any services it asks for in its parameters) and gives you whatever it returns; in this case, $get returns a function that, when called with a name, will alert the name with whatever we've set with setText:
myMod.run(function(greeting) {
greeting('Ford Prefect');
});
// Alerts: "Howdy there, Ford Prefect"
This is exactly how other providers, like $httpProvider and $routeProvider work.
For more information on providers and dependency injection in general, check out this SO question on dependency injection.

SailsJS blueprint matching wrong model

I'm having a tricky issue with my models and Sails' automatic blueprint matching.
When I post to one of my endpoints, /answer/create, I get a 500 response with validation errors for a different model entirely (the event model).
That endpoint for the event model would be at /event/create, but when I post to that I get a 404.
All of my files were generated with sails generate [model] and don't contain any custom Controller routes.
Has anyone seen this before?
It turned out this was a result of following this screencast:
http://irlnathan.github.io/sailscasts/blog/2013/09/15/sailscasts-answers-ep1-question-about-using-a-service-to-allow-inheritance-in-a-model/
I was inheriting a baseModel via services into more than one other model, so when I ran _.merge(), the changes it made would persist across them both, rendering inconsistencies.
If you want to do this, make sure you use the _.cloneDeep method to clone the base model, otherwise _.merge will affect it and your blueprints/actions won't work as expected.
In the screencast above, this would make line 12 in the user model look like this:
module.exports = _.merge(_.cloneDeep(baseModel) { ... });

Categories