Interpreting url path as params - node.js

I have faced upon a problem which may sound weird but lot of people must be dealing with.
I have defined two routes in my routes.js file.
Route1
router.route('/atpages/:query')
.get(app.oauth.authorise(), atpagesController.getAtpagesByIdOrName);
Route2
router.route('/atpages/match')
.get(app.oauth.authorise(), atpagesController.matchAtpagesByUrl);
Both the routes are supposed to call different functions in controller, but the issue is whenever i hit Route2 automatically Route1 is being called. I know the issue that it is considering match as a query, my problem is that i need the routes to hit different questions without redefining their endpoints.
What i want is route should remain the same as they are but they should hit respective functions only.
TIA.

Thanks, i resolved the issue.
In my function for route1 i have made a check that if i get query params as match, i am calling the function corresponding to route2.

Related

How do Express router.param and router.route work in terms of defining URL parameter names?

I created a router file using Express. The callback functions reside in their discrete "controllers" files. Following is an excerpt of the parts relevant to my question, and lines such as require of controller functions have been omitted:
const express = require('express');
const router = express.Router();
// This should run first
router.param('coolParamName', validateParamBeforeHandlingReqs);
// Param name is ↑↑↑↑↑ "consumed" here, although NOT defined yet
// This should run after the above code
router.route('/').get(getAllUserNames).post(createUser);
router.route('/:coolParamName').get(getUserName).patch(updateUser).delete(deleteUser);
// Param name is ↑↑↑↑↑ defined here, and was consumed earlier - how?
As the comments explain, it seems like the param name has been defined as coolParamName on the bottom, but "consumed" by the code written above it. This feels strange to me, because I feel it's natural to define first and then use later - is it just me? Did I write a code that's against the intended design pattern?
I would like to understand how Express defines the name of param, and how router.param and router.router handle them.
router.param('coolParamName') essentially registers a callback that will get called for any route (in that router) that uses the :coolParamName parameter and matches the current request. The callback will get called once per request BEFORE the route that matches the request that contains the :coolParamName parameter.
It's kind of like middleware for a matching parameter. It allows you to automatically configure some setup code anytime that particular parameter is matched in a route.
FYI, I expect that router.param() may be one of the least used features of Express since it can be accomplished many other ways, but it probably works best for validation-type code that checks named properties for validity before the route itself gets called.
You could accomplish the same thing by just using a piece of middleware on that specific route too or even just calling a function inside the route handler. So, this is just a nicety feature if you happen to use the same parameter in multiple routes.

console.log() not appearing in output for express server

I can't for the life of me figure out how to get console.log() to appear with my express server. It's a middle-tier API for our front-end. You'll have to forgive me if I speak about it a little awkwardly, I'm relatively inexperienced with these tools but I'll do my best to explain the issue despite my inexperience. I'm trying to use console.log to get a better idea of a rather complex projects behavior and what might be causing some issues with it in its current state. Unfortunately console.log only seems to work within plainjane examples like so:
export const routerExample = express.Router();
routerExample.use((req, res, next) => {
console.log('Time: ', Date.now()); // I show up in console just fine
next();
});
When I try to lookup the problem I'm experiencing all solutions seem to be regarding getting routing examples like the one above to appear in console, I can see such examples just fine. The problem comes from getting anything to show up in examples like:
// routing.ts
import { homeController } from '../controllers/homeController';
const homeEx: HomeExample = new HomeExample();
routerExample.get('/home', homeEx.getHome);
// homeController.ts
export class HomeExample {
public getHome (req: Request, res: Response) : void {
console.log("something is happening");
// do stuff
}
}
Any uses of console.log like above never appear anywhere in node's console (or elsewhere as far as I can tell).
What am I missing that is needed to make these log messages appear? This has to be incredibly simple but I've been through numerous similar sounding issues on stackoverflow and elsewhere and everything single of one of them seems to be describing slightly different issues (or misunderstandings) that don't solve my own issue. I've tried numerous versions of node/npm and have added the DEBUG:* flag as well. None of this seems to solve it. If I'm missing any code that'd help give context to the issue let me know. I've obviously cut down parts and renamed some objects as I can't exactly dump work-related code here. What am I missing? Any help would be greatly appreciated :)
Edit 1: since many similar posts to this seem to get this mixed up, no I'm not looking at my front-end's console or something for the output. I'm looking in the terminal window where I start the server from, where the router example does appear.
Edit 2: for reference, my file structure is something like:
app/
controllers/
homeController.ts (HomeExample stuff is here)
routes
routing.ts (routerExample stuff is here)
app.ts
...
Edit 3: the code works overall to be clear. the problem is explicitly that that log.console() isn't appearing, all the code I've wrapped into "// do stuff" is working as expected.
Checkout Express Middlewares
routerExample.get('/home', homeExample);
function homeExample (req: Request, res: Response, next:NextFunction) : void {
console.log("something is happening");
// do stuff
}
}
You are also calling a member of a non static or instantiated class see this:
TypeScript - Static
What you are missing is to create a new instance of the homeExample class. What I recommend is to export the new instance on the route file like this:
/routes/home.route.js
class HomeRoute {
/* your methods */
}
export default new HomeRoute();
then you can use it:
import homeRoutes from './routes/home.route';
router.get('/home', homeRoutes.getHome);
See the example:
https://replit.com/#abranhe/expressjs-console-log#index.js
After a fresh nights sleep I've figured it out. It, of course, was the most obvious problem that managed to slip by me in the overall complexity of the codebase. The /home call was deprecated and replaced with a different call in the front-end without mention in the middle-tier code that I had posted. I didn't even consider checking what was being called any deeper since I was experiencing the same issue with multiple other calls that I didn't include in the original post for brevity. Basically all the tools I'm working with here are completely new to me so my inexperience got the best of me. Thank you to #jfriend00 who made me double-take how /home was being called (it wasn't).
Since I was getting the data I needed without issue on the front-end I assumed these functions were being run, seeing as the data they produced was the same kind of data that was successfully being shown by the front-end, just without the console.log() output I added appearing.
Moral of the story: if every other question related to an issue on Stack Overflow concludes with "I just made a dumb mistake," take absolutely every precaution possible to observe what's happening, even if you feel like you already ruled out certain possibilities. Unfortunately I got a bit caught up with all the weird solutions I saw to the point where I got ahead of myself in debugging the problem.
I'm still a bit confused since the /home call specifically should still be "active" even if not called by the front-end, but console.log() is clearly working on other similar functions I've tested since figuring this out. There's either something hidden deep in the codebase that's breaking/overwriting /home and other old calls, or it's simply not being called right when I'm testing it outside of the front-end.
TLDR: I'm an idiot, every single API call I thought I was testing was not actually being called. Double-check your assumptions before asking for a specific solution.

Expressjs higher order router vs appending to request

Let's say I want to pass to an ExpressJS route callback an object.
I know I can append to app:
// router.js
const getFoo = (req, res) => res.json(req.app.foo);
// index.js
const app = express();
app.foo = {};
app.get('/foo', getFoo);
or I can use a higher order function:
// router.js
const getFoo = foo => (req, res) => res.json(foo);
// index.js
const app = express();
const foo = {};
app.get('/foo', getFoo(foo));
Both are easy to write, extend and test.
But, I don't know the implications of the solutions and whether one is better.
Is there anyone knowing real differences between the two approaches?
I think the second solution is more correct, here's why.
imagine you get used to the first solution and one day you need to send something called post or get or anything with the name of app property and you forget that there is already a property named like that, so you override original property without even realizing and when you call app.post() program will crash.
Believe me, you don't want hours of research wasted on something like that and realizing that you simply overrode original method
Also, in my opinion, it's always a bad idea mutating original object which wasn't generated by you
As #vahe-yavrumian mentioned it is not a good idea to mutate the state of the object created by a third party library.
between you can also use app.get() and app.set() methods to pass any data to the other routers in the queue (seems those methods are there just for this purpose.)
more information at https://expressjs.com/en/api.html.
The second solution easily allows you to pass different value for foo on different routes, if you ever found a need to do that.
The first solution essentially puts the value on the app singleton, which has all the implications of using singletons. (And as mentioned by #Anees, for express specifically the app settings with get and set are the proper place to store this, not a custom property)

Express array of routes

The express documentation says that you can supply an array for the path variable in app.use(). I'm trying to have all routes in the routes array point back to the static folder in the static variable.
Here's what I have:
const static = express.static(path.join(__dirname, '../build'));
const routes = ['/','/projects','/project1','/blog']
app.use(routes, static);
These routes all do work correctly with the static folder variable if I write a seperate app.use() function for each route, but it would be a lot more maintainable if I can get the routes in an array like in the documentation. The documentation doesn't show any examples suggesting that I need to map through the array.
So, it appears (based on experimentation as this is not in the doc), that when you pass an array of routes, Express finds the first item in the array that matches the current path and calls the route handler for that one only. Even if there are other routes that would also match (as in your case), it does not call the route handler again for those.
So, because '/' matches ALL routes when used with app.use(), it will always use that one and thus the others will never work properly when using the array, but do work properly when listed as individual routes. You can fix it by making sure that the most permissive routes are last. So, change your array to this:
const static = express.static(path.join(__dirname, '../build'));
const routes = ['/projects','/project1','/blog', '/'];
app.use(routes, static);
This moves '/' to the end so the others get a chance to match before the '/' is looked at and . '/' will only get used when the path wasn't one of the others.
FYI, this is a bit of a weird structure where you're directing multiple URL path prefixes all to the same ../build directory for express.static() to look there. There's no reason you need to do it that way. This appears to be an artificially created issue because of your URL design. Doing it this way will cause URLs like /x.js, /projects/x.js and /blog/x.js to resolve to the exact same x.js file in the ../build directory.

Express route wrong match

I've read up other questions on people's routes mismatching and then ordering the routes solving the problem. I've got this problem where my URL route is being treated as a parameter and then express mismatches and leads to the wrong route. e.g. here are the two routes:
app.get('/byASIN/LowPrice/:asin/:price',function(req,res){});
and
app.get('/byASIN/:asin/:price', function(req, res) {});
Now all works fine but as soon as I take any param out of the first route it matches the route given below which is not what I want.
If I hit /byASIN/LowPrice/:asin/:price everything works fine but as soon as I hit /byASIN/LowPrice/:asin it matches byASIN/:asin/:price and hence calls the wrong function and crashes my server. I would like to have them match explicitly and if /byASIN/LowPrice/:asin is called, respond with some warning e.g. you're calling with one less argument. What am I missing here?
By default express Url parameters are not optinial, this is why
app.get('/byASIN/LowPrice/:asin/:price',function(req,res){});
does not match /byASIN/LowPrice/:asin, because the second parameter is missing.
However you can make a parameter optional by adding a ? to it:
app.get('/byASIN/LowPrice/:asin/:price?',function(req,res){});
this should solve your problem.
Try to define a route for /byASIN/LowPrice/:asin/:price to handle, then use a wildcard to handle everything else.
app.get('/byASIN/LowPrice/:asin/:price',function(req,res){});
app.get('*',function(req,res){});
Express matches the route by the order you insert them. If you have the loosely routes defined first, then express will use that one as the match first. An extreme example would be
app.get('*', function(req, res) {});
If this was defined as the first route, then no other route will be called (if without calling next()).
If you want express to always use the strict one first, then you will need to change the order of your routes by having the strict ones defined before the loosely ones.
It'd be nice if express support priority in the route, which could be a good solution for your problem, but until then, unfortunately, this can be fixed by ordering only :(

Resources