Define koa-router nested routes w/ prefixes - node.js

I'm trying to define different routes using koa-router and I'm having a hellova time getting it working.
Something like this:
const apiRouter = new KoaRouter({
prefix: '/api'
})
.use(bodyParser)
.post('/sign-in', signinMiddleware)
.get('auth-check', authCheckMiddleware)
const protectedApisRouter = new KoaRouter()
.use(authorizeMiddleware)
.get('/widgets', getWidgetsListMiddleware)
.post('/widgets', createWidgetMiddleware)
.get('/widgets/:widgetId', getWidgetByIdMiddleware)
.patch('/widgets/:widgetId', updateWidgetMiddleware)
apiRouter.use(
prodectedApisRouter.routes(),
prodectedApisRouter.allowedMethods()
)
koaApp.use(apiRouter.routes())
koaApp.use(apiRouter.allowedMethods())
I would expect that an requests to /api/widgets/* SHOULD enter into their respective middleware, after the bodyParser and authorizeMiddleware middleware` run, based on the documentation here: https://github.com/alexmingoia/koa-router#nested-routers
But instead, I'm getting 404's for all of those routes. What am I doing wrong?

Apparently the above code works just fine.. but in my authorizeMiddleware I was doing await next instead of await next() 🤦
Too bad there isn't a way to delete questions on here.. people are now gonna come here for issues not related to my idiocy.

Related

Cannot get query param in nodejs

I have this proxy-middleware application. It is not including express.js.
Server.js contains this:
const app = require('connect')(),
http = require('http'),
While the middlewares a set of rules, for example:
const httpProxy = require('http-proxy'),
HttpProxyRules = require('http-proxy-rules'),
const proxyRules = new HttpProxyRules({
rules: {
'/api/v1/stuff/([0-9]+)/documents/': 'http://0.0.0.0:3000/$1',
},
default: 'http://localhost:4443'
});
So all the other microservices are being intercepted by this proxy.
There is an "app.use" where a few checks are made.
Here I can see the request-object. Im interested in reading the query parameter attached to the url.
So when I have this:
http://localhost:8081/api/v1.1/stuff/63/documents/file.pdf?token=mytoken
Printing this:
console.log('GATEWAY',req.originalUrl);
Will output this:
http://localhost:8081/api/v1.1/stuff/63/documents/file.pdf?token=mytoken
However, how can I access the query parameter? As Im not using express, doing "req.query" gives undefined.
I have tried a bunch of solutions: "querystring", "url" etc. But they give very strange result and it is not easy to get the field itself. I can never do something like:
req.query
I had a look at connect documentation but there is nothing about getting the request query parameters.
What should I use?
Please check if you used the followings:
If not, please add them:
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
And try to get req.params

TypeError: register.route(...).use is not a function

I'm using express.Router() whenever i try use method it gives the following error
TypeError: register.route(...).use is not a function
Code
/server/routes
const express = require('express');
const register = express.Router();
const account = require("../controller/AccountController");
const Middleware = require("../utils/middlewares");
register.route('/')
.post(Middleware.checkUser)
.post(account.user_register)
register.route('/verify/:token')
.get(Middleware.verifyEmail)
register.route('/resend/:email')
.use(Middleware.sendVerification)
module.exports = register;
Server.js
server.use('/register', register);
When i use a method like get there is no error. But i don't want to use any method since the middleware just sends an email
As stated in documentation, route all method is intended for route-specific middleware, it could be:
register.route('/resend/:email')
.all(Middleware.sendVerification)
If the route is expected to be requested with GET only and may not make sense for other verbs, it should be narrowed down to supported verbs:
register.route('/resend/:email')
.get(Middleware.sendVerification)
In this case sendVerification seems to be route handler and not a middleware. It's suitable to specify it only for get if /resend/ is expected to be requested with GET.

How to use single piece of middleware with more than one express router?

I am working on a university project and we have decided to go for MEAN technology stack. To be honest I am a beginner with NodeJS and express, more precisely this is the first time I do sth with it.
I've found that is preferable to use express.Router rather than putting all routes to express instance e.g. app.post('path', function(req, res) { ... })
So this is what I have
var express = require('express');
var app = express();
function authorizationMiddleware(req, res, next) {
...
}
// handles login, doesn't meed autorizationMiddleware
var authRouter = express.Router();
authRouter.route('/login')
.post(function (req, res) {
...
});
// handles fetching of a single, all person(s), fetching of transactions for a person
var personRouter = require('./routes/personRoutes')(Person, Transaction, autorizationMiddleware);
//handles adding of a new transaction e.g. POST /api/transactions where params such as sender, recipient and amount are passed in body
var transactionRouther = require('./routes/transactionRoutes')(Person, Transaction, autorizationMiddleware);
app.use('/api', authRouter);
app.use('/api/persons', personRouter);
app.use('/api/transactions', transactionRoutes);
app.listen(8080, function () {
console.log('Listening on port: ' + 8080);
});
As you can see I have three routers (not even sure if I have gonne too far with them), authRouter is handling login only and I have also decided to separate persons logic from transactions logic too. (maybe I could have handled creation of new transaction in a way like /api/persons/:personId/transactions but I rather liked the idea of sending all required params in body).
I would like to ask if you agree with the solution I tried. As you can see I am passing authrizationMiddleware function (handles verification of JWT token) function to router modules and using it there.
Is there maybe a better way to use the same middleware with of multiple routers or is this a legit way?
Thx in advance
Cheers!
I don't get why you use 3 Routers. The "common" way to go (or at least the way I go) is to put all the routes in the same place, except when the path is very different or the purpose is different (for example I separate the error routes from the others).
For example, let's say I need to build a rest api for an app, I would probably have paths like:
/users/:userid
/users/:userid/comments/:commentid
/locations
...
All these routes can go in the same Router and if you want, you can apply specific authentication/authorization middlewares to them:
router.get("/users/:userid",
doAuthentication, authorizeOnUserId,
userController.getUserById);
router.get("/locations",
doAuthentication, authorizeLocations,
locationController.getAllLocations);
The middlewares are called in sequence and the request is passed on to the next middleware only if there are no errors (unauthenticaed/ unhauthorized).
Then you can simply import your routes like this:
app.use('/api', router);
Using this technique allows you to have a fine grain control over your routes.
Hope this helps.

404 when accessing new route

I'm trying to add a new route (/profile) to my NodeJS Express web application. I've modified my app.js file like this:
var routes = require('./routes/index');
var profile = require('./routes/profile');
app.use('/', routes);
app.use('/profile', profile);
The '/' index path works fine, my issue is with '/profile'. Whenever I try to access it, I get a 404. This is profile.js:
var express = require('express');
var router = express.Router();
router.get('/profile', function(req, res) {
var username = req.session.username;
if(username) {
res.render('profile');
} else {
res.redirect('/login');
}
});
module.exports = router;
I don't understand what I'm doing wrong because in the example express application that is generated, '/users' works fine. I basically copied that format, but it's throwing a 404. Any ideas?
In my profile.js, I had to change my GET request path to this:
router.get('/', function(req, res) {
//code
});
Otherwise, the router would be looking for /profile/profile. When I change it to /, it's just looking for the root of `/profile', or at least that's how I understand it.
To understand what you are doing wrong you should know that Node.js uses middleware functions to route your requests. To simplify you can think about it as a chain of functions.
Middleware is like a plumbing pipe, requests start at the first middleware you define and work their way “down” the middleware stack processing for each path they match.
So with the following statement you added a middleware function to handle any request starting with the root path /profile, and it is a common pattern in Node to use the use method to define the root paths.
app.use('/profile', profile);
The use method is doing part of the routing in your scenario and the statement above will match any route starting with that path, including /profile/all or /profile/12 or even /profile/go/deeper/inside.
However, you want to narrow down that routing to something more specific, so that is why you pass a router middleware function (profile in your case) to match more specific routes instead of all routes starting with /profile.
The profile middleware function is actually the next step in the chain of functions to execute, and it will start from the root path specified in the use statement, which is the reason why you need to start again with / and not with /profile. If you wanted to match a profile by ID you would do:
router.get('/:id', ...)
Which would be concatenated with the base URL (from the /use statement) and would match a request like /profile/2 or /profile/abc.

Deleting posted content using $resource in AngularJS

I am new to AngularJS and am trying out a few things with posting and deleting content using $resource. I've got the posting working fine, but when I try to delete something that I've posted I get a 404 error.
DELETE http://localhost:3000/tasks?__v=0&_id=53c5ddcf2978af0000ccdc50&beginningDat…vacy=true&title=This+is+a+complete+task&website=http:%2F%2Fwww.hotmail.com 404 (Not Found)
I've been working on this for a few days now and I'm just not seeing what i am missing. I am using a MEAN stack. I've got mongoose, express, bodyParser, and cors as dependencies in my app.js and created my endpoints:
app.get('/tasks', api.getTask);
app.post('/tasks', api.postTask);
app.delete('/tasks/:_id', api.deleteTask);
Here is the code from my api.js which
exports.deleteTask = function(req, res){
var _id = req.params._id;
Task.remove({_id:_id}, function(err, task){
res.send(task + ' removed task successfully');
if(err){
res.send('Hey guys...he is still here.');
}
});
};
Here is my factory/service:
'use strict';
angular.module('achievementApp').factory('tasks', function($resource){
return $resource('http://localhost:3000/tasks/',{_id: '#_id'},{
get: {method:'GET', isArray: true},
add: {method:'POST'},
delete: {method: 'DELETE'}
});
});
And here is the code from the Ctrl:
$scope.taskList = tasks.get({});
$scope.removeTask = function(obj){
tasks.delete(obj);
var index = $scope.taskList.indexOf(obj);
console.log(index);
$scope.taskList.splice(index,1);
console.log('removeTask was called');
};
Any guidance on this would be greatly appreciated. I've tried just about everything I can to get it to work and have had no luck so far.
It looks like you have a mismatch between the angular code which is putting the _id in the query string and the express code which is looking for it as a route param, which looks in the path part of the URL. req.params comes from the path part before the ?. req.query comes from the query string part after the ?. It would be more conventional to use the path in terms of REST, so I suggest changing your angularjs code to have /tasks/:_id as the resource route.
Aside: Best to use relative paths in your browser JS and omit the protocol, host, and port. Otherwise your app won't work when you deploy it on the real web.

Resources