loopback add item to property array by api not inject ctx - node.js

In loopbackjs the follow api not working.
PUT /Bookstore/{id}/books/rel/{fk} Add a related item by id for books.
I define in phase of boot
app.remotes().before('*.*', inject);
app.remotes().before('*.prototype.*', function(ctx, instance, next) {
inject(ctx, next);
});
function inject add to options more information.
But in this case the first operation is
Model.observe('access',.... define in mixin and not inject.
The api fail.
Someone has already had the same problem?

Related

Fastify point-of-view local variables

I am switching from Express.js to Fastify. I need to do it quickly, so using only API is impossible yet. Haven't written React app.
My problem is: I am using point-of-view and I don't know how to pass local variable to all requests. In express there something like
app.use(function (req, res, next)
{
res.local.new_notifications = 50;
})
and I can get it in template engine on every page like
<%= new_notifications %>
Is there something like this in Fastify + point-of-view?
You can, I am not sure if it is a new implementation or not since you asked a solution more than 1 year ago, this is for future viewers.
You simple have to add a .locals object and attach to it anything you want available with your engine.
This is from the documentation:
If you want to provide data, which will be depended on by a request and available in all views, you have to add property locals to reply object, like in the example below:
fastify.addHook('preHandler', function (request, reply, done) {
reply.locals = {
text: getTextFromRequest(request) // it will be available in all views
}
done()
})
Be sure to create the object with the assignment {} since locals its not defined!

Koa-router getting parsed params before hitting route

I'm using koa2 and koa-router together with sequelize on top. I want to be able to control user access based on their roles in the database, and it's been working somewhat so far. I made my own RBAC implementation, but I'm having some trouble.
I need to quit execution BEFORE any endpoint is hit if the user doesn't have access, considering endpoints can do any action (like inserting a new item etc.). This makes perfect sense, I realize I could potentially use transactions with Sequelize, but I find that would add more overhead and deadline is closing in.
My implementation so far looks somewhat like the following:
// initialize.js
initalizeRoutes()
initializeServerMiddleware()
Server middleware is registered after routes.
// function initializeRoutes
app.router = require('koa-router')
app.router.use('*', access_control(app))
require('./routes_init')
routes_init just runs a function which recursively parses a folder and imports all middleware definitions.
// function initializeServerMiddleware
// blah blah bunch of middleware
app.server.use(app.router.routes()).use(app.router.allowedMethods())
This is just regular koa-router.
However, the issue arises in access_control.
I have one file (access_control_definitions.js) where I specify named routes, their respective sequelize model name, and what rules exists for the route. (e.g. what role, if the owner is able to access their own resource...) I calculate whether the requester owns a resource by a route param (e.g. resource ID is ctx.params.id). However, in this implementation, params don't seem to be parsed. I don't think it's right that I have to manually parse the params before koa-router does it. Is anyone able to identify a better way based on this that would solve ctx.params not being filled with the actual named parameter?
edit: I also created a GitHub issue for this, considering it seems to me like there's some funny business going on.
So if you look at router.js
layerChain = matchedLayers.reduce(function(memo, layer) {
memo.push(function(ctx, next) {
ctx.captures = layer.captures(path, ctx.captures);
ctx.params = layer.params(path, ctx.captures, ctx.params);
ctx.routerName = layer.name;
return next();
});
return memo.concat(layer.stack);
}, []);
return compose(layerChain)(ctx, next);
What it does is that for every route function that you have, it add its own capturing layer to generate the params
Now this actually does make sense because you can have two middleware for same url with different parameters
router.use('/abc/:did', (ctx, next) => {
// ctx.router available
console.log('my request came here too', ctx.params.did)
if (next)
next();
});
router.get('/abc/:id', (ctx, next) => {
console.log('my request came here', ctx.params.id)
});
Now for the first handler a parameter id makes no sense and for the second one parameter did doesn't make any sense. Which means these parameters are specific to a handler and only make sense inside the handler. That is why it makes sense to not have the params that you expect to be there. I don't think it is a bug
And since you already found the workaround
const fromRouteId = pathToRegexp(ctx._matchedRoute).exec(ctx.captures[0])
You should use the same. Or a better one might be
var lastMatch = ctx.matched[ctx.matched.length-1];
params = lastMatch.params(ctx.originalUrl, lastMatch.captures(ctx.originalUrl), {})

Node Custom error handler middleware optimization?

Hej,
I am working on an enabler within a nodeJS platform which expose a REST API. I need to implement some custom errors handlers whose will implement some business logic.
Basically, I have to deal with a POST request whose content is encoded as application/json. I expect to receive a list of objects representing a services within an array. Each item of the array is an object which does have the following structure
{
"code": "sampleCode",
"id": "someId",
"status: "someStatus"
}
In term of business logic, each service belongs to a family. And each family does have some configurations stored in a properties file.
Once I received the request I need to check the body content and apply some custom rules which will throw custom errors.
Rules are :
"code value can't be empty.
"code" value must be in a list defined in the properties
"code" if code value belongs to family foo or bar id can't be empty
I implemented some small unit functions using lodash to do tests in collections.
My main regards is that do I need to go async for those process ? For now I am just calling the functions as it. As i am quite new to nodeJs and especially in terms of best practice in corporate environement I would like to have opinions from more experienced users ?
Do custom errors handlers implementing business logic should be async or there's no benefits ?
Nodejs is fundamentally asynchronous, so in general, you don't do sync stuff.
If you use expressjs or similar, you should create a kind of "filter" before the request, something like :
function validator(req, res, next){
//my validation code
next();
}
app.get('/my/endpoint', validator, function (req, res) {
req.send();
});

Loopback update model after save

I want to update last inserted/updated document(row) in "after save" hook without create new instance of that like this:
Model.observe('after save', function (ctx, next) {
ctx.someProperty = 'Foo';
ctx.update();
});
How it possible?
I'm not sure what you mean by 'update' a model. As far as I know, there is no update() function on the generic model class. If you're looking for updateAttribute then the documentation on that functionality is here.
However, assuming your question is just "How do I access the observed model inside of a loopback hook?" then the answer is that the instance is stored at ctx.instance rather that returned as ctx variable itself. See the examples here.
E.g.
Model.observe('after save', function (ctx, next) {
ctx.instance.updateAttributes({someProperty: 'Foo'})
});
If you can describe in more detail the functionality you're trying to achieve with the update() function, I will try to address that question. Note also that the code above would probably cause an infinite loop - because the updateAttribute call will itself trigger the 'after save' hook - which is another reason why I'm not so sure what you're really asking.

Loopback beforeRemote for PUT requests

Using Loopback framework, I want to perform some operations before the Item is edited hence I am trying this but unable to bind this to the update hook.
Item.beforeRemote("update", function(ctx,myitem,next) {
console.log("inside update");
});
Instead of update I have tried with updateAttributes,updateById, create but none works. This kind of beforeRemote hook works well with create on POST, but unable to get it with PUT during edit.
The last solution left with me is again inspect the methodString with wildcard hook but I want to know if there is anything documented which I could not find.
Item.beforeRemote("**",function(ctx,instance,next){
console.log("inside update");
});
I know that two year have passed since this post was opened, but if any body have the same question and if you use the endpoint your_model/{id} the afterRemote hook is replaceById.
If you need to know which method is fired in remote hook use this code:
yourModel.beforeRemote('**', function(ctx, unused, next) {
console.info('Method name: ', ctx.method.name);
next();
});
Contrary to the comments, save is a remote hook, not an operation hook, but you want to use it as: prototype.save. The relevant operational hook would be before save. You can see a table of these on the LoopBack docs page. I would probably implement this as an operational hook though, and use the isNewInstance property on the context to only perform the action on update:
Item.observe('before save', function(ctx, next) {
if (ctx.isNewInstance) {
// do something with ctx.currentInstance
}
next();
});
Sorry for bumping into old question but its for those who are still searching.
'prototype.updateAttributes' can be used as remote hook for update requests.
and #jakerella , there is no remote hook called 'save' , i myself tried it, but didnt work.
Came here looking for another thing, guess it will be helpful to someone.
For before remote model/:id patch method you have to use "prototype.patchAttributes".
On loopback3 for PATCH you can use "prototype.patchAttributes" to sanitize your data before the update.
YourModel.beforeRemote('prototype.patchAttributes', (ctx, unused, next) => {
console.log(ctx.args.data);
next();
});

Resources