Is it good practice to internally call an API within the server? - node.js

I have some codes as follows
// [DELETE] /api/v1/authors/:id
async deleteAuthor(req, res) {
const author = await Author.findByIdAndRemove(req.params.id);
// delete blogs of the author
axios.delete(
`http://localhost:${process.env.PORT}/api/v1/blogs/author/${author._id}`,
{
headers: {
Authorization: req.headers.authorization
}
}
);
res.status(200).send();
}
By this, I want to delete an author and all their blogs. I know the naming of the uri is not good but is it overall a good way to code like this or there are other ways to do the same thing. I'm using Node.js and Mongoose

I think is not a good practice, you should avoid making circular HTTP Calls (Not optimal, can unnecessary duplicate logic, and is more difficult to read your code)
What do I suggest? Following DDD & Hex. Architecture:
Have separated services e.g. RemoveAuthor & RemoveAuthorBlogs
(Following the SRP principle of SOLID, one service do only one
thing)
Your HTTP endpoints (E.g. DELETE /author/:id & DELETE /blogs/author/:id) will invoke those services.
If you need to delete the author and his blogs in the same request.
a. Create a high service that calls to RemoveAuthor and RemoveAuthorBlogs (E.g. RemoveAuthorReferences)
b. (My vote is for this) => The service RemoveAuthor remove from DB the author as the first step and as the second step will dispatch a domain event (E.g. AuthorDeleted that will be listened to by an EventHandler (E.g. DeleteBlogsOnAuthorDeleted who will remove the blogs of the author)
In my opinion, your services shouldn't make internal HTTP requests when you can use your own services, in this way, if the logic changes, you'll only need to modify the service

Related

Writing all your functions in one cloud function

What if I put multiple function inside a single cloud function so that its instance lives at max and that I will have to deal with cold start once?
Why is this a bad idea?
export const shop = functions.https.onCall(async (data, context) => {
switch (data.type) {
case "get_fruits":
return await getFruits();
case "place_order":
return await placeOrder();
case "add_to_cart":
return await addToCart();
default:
return;
}
});
It will work but, IMO, it's not a good thing to do. There are many principles and patterns that exist today and that you do not enforce your solution.
Microservice
One of them is the split in microservices. There is no problem to build a monolith, but when I'm seeing your example (get_fruit, place_order, add_to_cart), I'm seeing different roles and responsibilities. I love the separation of concern: 1 service does 1 thing.
Routing
But, maybe your service is only a service for the routing and call functions deployed independently (and you enforce the microservice principle). If so, your service can become a bottleneck, if there are a lot of entries and a lot of queries.
In addition, there are services dedicated for routing: load balancers. They use the URL path of the requests and reach the correct microservices to serve them
Developer usage
Yes a URL, not a field in the body of your message to route the traffic. Today, the developers are familiar with the REST API. To get the fruit, they perform a GET request to the /fruit URL and they know they will get the fruits. If they want to add to the cart, they perform a POST request to the /cart URL and it works!
You USE URL, standard REST definition, load balancers and microservices.
You can imagine other benefits:
Each microservice can scale independently (you can have more get_fruit request than place_order, the service scale differently)
The security is easier to control (no security to get the catalog (fruits)), but you have to be authenticated to place an order
Evolution velocity can be decoupled between the services
...

Clean Architecture - How to handle usecase dependencies

I am refactoring one of my older applications around to using the concept of use cases "clean architecture".
I am little confused on how to leverage the common data & entity validations
for e.g. There are 2 use cases
Allow admins to import a new workflow template
Allow admins to create new workflow template
The above use cases are called from the controllers.
In both the above cases, there are some common database level validations like:
Is there already a workflow with same name ?
To handle these validations, Do I make this as separate use-case like "checkIfWorkflowWithSameNameExists()" ?
If I make a separate use case, then what options are better to call these common validations
Can one use case call another use case directly
export function importNewWorkflowTemplate(specs){
const { workflowRepository } = specs;
const exists = checkIfWorkflowWithSameNameExists()
if(exists){
//return error
}
return new (payLoad) => {
//logic
}
}
Should I be injecting the dependent use cases
export function importNewWorkflowTemplate(specs){
const { workflowRepository, checkIfWorkflowWithSameNameExists } = specs;
return new (payLoad) => {
//logic
}
}
Should the validation belong to outer layer like the controller?
What you describe - checkIfWorkflowWithSameNameExists() - is not a use case.
This is simply a method put on a domain service, such as a repository. This could be a repository method on your workflow repository such as hasWorkflowWithName(name). The repository represents a collection of aggregates and thus knows best if there is one with the same name already.
If there is more complex domain logic to check for an existing repository and then either perform some error handling or performing specific logic to create the logic you can also consider to encapsulate these steps inside a domain service method. In this case the workflow repository interface would be injected into the workflow domain service which would then again be injected into the use cases.
With that you could use the same domain service in both use cases and the use case are responsible to orchestrate the translation between the external commands and the domain service interface and the corresponding domain entities.

Feathers JS nested Routing or creating alternate services

The project I'm working on uses the feathers JS framework server side. Many of the services have hooks (or middleware) that make other calls and attach data before sending back to the client. If I have a new feature that needs to query a database but for a only few specific things I'm thinking I don't want to use the already built out "find" method for this database query as that "find" method has many other unneeded hooks and calls to other databases to get data I do not need for this new query on my feature.
My two solutions so far:
I could use the standard "find" query and just write if statements in all hooks that check for a specific string parameter that can be passed in on client side so these hooks are deactivated on this specific call but that seems tedious especially if I find this need for several other different services that have already been built out.
I initialize a second service below my main service so if my main service is:
app.use('/comments', new JHService(options));
right underneath I write:
app.use('/comments/allParticipants', new JHService(options));
And then attach a whole new set of hooks for that service. Basically it's a whole new service with the only relation to the origin in that the first part of it's name is 'comments' Since I'm new to feathers I'm not sure if that is a performant or optimal solution.
Is there a better solution then those options? or is option 1 or option 2 the most correct way to solve my current issue?
You can always wrap the population hooks into a conditional hook:
const hooks = require('feathers-hooks-common');
app.service('myservice').after({
create: hooks.iff(hook => hook.params.populate !== false, populateEntries)
});
Now population will only run if params.populate is not false.

ACL in Node js based on relationships

I'm trying to implement some kind of permission framework in Node js, using sequelize as an ORM (with Postgres). After hours of research, the closest thing I can find to do this with existing npm modules is using acl with acl sequelize to support my stack.
The problem is that it looks like the acl module assigns a role, where that role would get a set of permissions to all instances of a specific resource. However, I need to do permissioning for instances based on existing relationships of that user.
As an example, consider a permissioning system for a simple forum. It gives these permissions for each role:
// allow guests to view posts
acl.allow("guest", "post", "view");
// allow registered users to view and create posts
acl.allow("registered users", "post", ["view", "create"]);
// allow administrators to perform any action on posts
acl.allow("administrator", "post", "*");
Suppose that I want to also add the ability for registered users to also edit their own posts, and the user has a relationship to all the posts they've created.
Is there any way for this module to do this, or any other module that can support this kind of behavior on the database / ORM level?
If not, and I have to implement a custom one, what would the best approach to creating something like this.
There is relatively new library CASL. I'm the author of this library. And it's possible to implement your usecase quite easily:
const { AbilityBuilder } = require('casl')
const ability = AbilityBuilder.define((can, cannot) => {
can('read', 'all')
can(['update', 'delete'], 'Article', { author_id: loggedInUser.id })
})
The code above basically says:
- anyone can read everything
- anyone can update and delete articles where author_id equals logged in user id
Later you can do:
ability.can('delete', 'Post')
//or
ability.can('update', post)
// where post variable is an instance of your Post model
Also there is an article which explains how to integrate CASL with MongoDB and Express exactly for your usecase.

Can I or Should I use a Global variable in Angularjs to store a logged in user?

I'm new to angular and developing my first 'real' application. I'm trying to build a calendar/scheduling app ( source code can all be seen on github ) and I want to be able to change the content if there is a user logged in (i.e. display details relevant to them) but here's the catch:
I don't want the app to be dependent on having a logged in user ( needs to be something that can be configured to work publicly, privately or both)
I don't want to implement the user/login within this app if it can be avoided ( I want to eventually include my app in another app where this might be implemented but isn't necessarily implemented using any particular security frameworks or limited to any)
I had an idea of creating some global variable user that could be referenced through out my application, or if I had to implement a system to do it all in this app that I could do so in in some abstract way so that different options could be injected in.
some of my ideas or understanding of what I should be doing may be completely wrong and ignorant of fundamentals but I genuinely do not know what approach I should take to do this.
In case it is relevant I currently don't have any back-end but eventually hope use MongoDB for storage and nodejs for services but I also want to try keep it open-ended to allow others to use different storage/backends such as sql and php
is there away to have a global uservariable/service that I could inject/populate from another (parent?) app?
If so what would be the best approach to do so?
If Not, why and what approach should I take and why?
Update
I Believe from comments online and some suggestion made to me that a service would be the best option BUT How would I go about injecting from a parent application into this applications service?
If your (single) page is rendered dynamically by the server and the server knows if you are logged-in or not, then you could do the following:
Dynamically render a script tag that produces:
<script>
window.user = { id: 1234, name: 'User A', isLoggedIn: true };
</script>
For non logged-in users:
<script>
window.user = { isLoggedIn: false };
</script>
For convinience, copy user to a value inside angular's IOC:
angular.module('myApp').value('user', window.user);
Then, you can use it in DI:
angular.module('myApp').factory('myService', function(user) {
return {
doSomething: function() {
if (user.isLoggedIn) {
...
} else {
...
}
}
};
});
Something tricky (which you should thing twice before doing [SEE COMMENTS]) is extending the $scope:
angular.module('myApp').config(function($provide) {
$provide.decorator('$controller', function($delegate, user) {
return function(constructor, locals) {
locals.$scope._user = user;
return $delegate(constructor, locals);
};
});
});
This piece of code decorates the $controller service (responsible for contructing controllers) and basically says that $scope objects prior to being passed to controllers, will be enhanced with the _user property.
Having it automatically $scoped means that you can directly use it any view, anywhere:
<div ng-if="_user.isLoggedIn">Content only for logged-in users</div>
This is something risky since you may end up running into naming conflicts with the original $scope API or properties that you add in your controllers.
It goes without saying that these stuff run solely in the client and they can be easily tampered. Your server-side code should always check the user and return the correct data subset or accept the right actions.
Yes you can do it in $rootScope. However, I believe it's better practice to put it inside a service. Services are singletons meaning they maintain the same state throughout the application and as such are prefect for storing things like a user object. Using a "user" service instead of $rootScope is just better organization in my opinion. Although technically you can achieve the same results, generally speaking you don't want to over-populate your $rootScope with functionality.
You can have a global user object inside the $rootScope and have it injected in all your controllers by simply putting it into the arguments of the controller, just as you do with $scope. Then you can implement functionalities in a simple check: if($rootScope.user). This allows you to model the user object in any way you want and where you want, acting as a global variable, inside of Angular's domain and good practices with DI.
Just to add on my comment and your edit. Here is what the code would look like if you wanted to be able to re-use your user service and insert it into other apps.
angular.module('user', []).service('userService', [function(){
//declare your user properties and methods
}])
angular.module('myApp', ['user'])
.controller('myCtrl', ['userService', '$scope', function(userService, scope){
// you can access userService from here
}])
Not sure if that's what you wanted but likewise you could have your "user" module have a dependency to another "parent" module and access that module's data the same way.

Resources