express differentiate dynamic segment and other static segment - node.js

const route = Router();
router.get('user/:UserId');
router.post('user/:UserId');
router.put('user/:UserId');
I have the above routes for user REST api's, i wanted to have a user by location api. so i tried something like below.
router.get('user/getUserByLocation');
The problem is it calls the router.get('user/:UserId'); as it finds the getUserByLocation as UserId.
I changed the router.get('user/getUserByLocation'); to router.get('getUserByLocation'); it works.
My question is , is the above solution the best approach or i need to use the router.get('user'); with querystring to get the getUserByLocation.

You can declare in router that :UserId should be a number (and not a string) using (\\d+). This way user/getUserByLocation shouldnt match your route user/:UserId:
const route = Router();
router.get('user/:UserId(\\d+)');
router.post('user/:UserId(\\d+)');
router.put('user/:UserId(\\d+)');
router.get('user/getUserByLocation');

You have to configre getUserByLocation before router.get('user/:UserId'). Change the order of your code. Configure router.get('user/getUserByLocation') first.
In express, the first matching request handler is executed, so if router.get('user/getUserByLocation') is configured after router.get('user/:UserId'). That will get called before that.
const route = Router();
router.get('user/getUserByLocation');
router.get('user/:UserId');
router.post('user/:UserId');
router.put('user/:UserId');

Related

need to account for params being blank in node express app

i have a node/express app, and need to account for if users don't enter a stationId param in the Url. Ive been looking at all sorts of regex built in to express, adding express-validator but couldnt get it to work for middleware. what would be the best way to do this? is the something in express or a third party validator?
The stationId is an mix of letters and numbers 1491TH it is used to call a second API to get information about the station entered in the Url. Im trying to work out how to use a validator to check if the param is blank, or if its not in the format i want.
import express from 'express'
import axios from 'axios'
import { cleanseLocation } from './utils.js'
const PORT = 3000
const app = express()
app.get('/:stationId/asset',(req, res) => {
const stationId = cleanseLocation(req.params.stationId)
const resp = await axios.get(`https://online-api/id/stations/${stationId}`)
res.send(resp)
})
app.listen(PORT, () =>
console.log(`The node API is running on: http://localhost:${PORT}.`)
)
When you define a route like:
app.get('/:stationId/asset', ...)
You are using a wildcard for the first path segment. It will match ANYTHING in the first path segment. So, it will match all of these:
/play/asset
/1238576/asset
/%20/asset
It will not match:
/asset
because that's just one path segment.
If you have specific rules for what is and isn't a valid stationId, you can implement a check for those inside the route handler:
app.get('/:stationId/asset',(req, res) => {
if (some logic to check req.param.stationId) {
res.send('working');
} else {
res.status(404).send("Invalid stationId");
}
});
If you need further help with how to implement stationId checking, you will have to disclose exactly how you would tell if it's a valid stationId or not.
is the something in express or a third party validator?
What mechanism to use for implementing validation depends entirely upon how you determine whether it is or isn't a valid stationId. You would have to explain that algorithm or method for us to help further.
The stationId is an mix of letters and numbers 1491TH it is used to call a second API to get information about the station entered in the Url. Im trying to work out how to use a validator to check if the param is blank, or if its not in the format i want
It already can't be blank. It won't match the route if there's no stationId at all.
So, if you are looking for a sequence of letters and numbers, you can just use a regex and define the route such that it will only match the route definition if you get a legal format for a stationId:
app.get('/:stationId([A-Za-z0-9]+)/asset', ...)
FYI, here's a helpful article with good example on using regex in Express routes. The full doc for what you can do with a regex in a route definition is here in the path-to-regexp documentation which is the module Express uses for this feature.

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.

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)

NodeJS, express - routing

I´ve setup a little NodeJS API with 'expressjs'. In my code I have some similar code blocks looking like this:
app.get('/api/my/path', (req, res) => {
doSomethingUseful();
});
All thoses requests do something different, so the function being called is always different too.
I´m asking myself if this is good practice for writing code in NodeJS or not.
If there is a better / cleaner way of writing all those paths I would appreciate if you could tell me how or at least giving me a place to look for it.
EDIT 1: To be clear: I ask if it´s a good idea to write a lot of 'app.get(...)' into one source code file or if there is a better way?
Yes there is a better way than writing all routes in one file. For example, let us say you have routes for users and questions.
For users, you want get/set/put/delete for profile, and similarly for questions.So you create the following folder structure: /api/users and /api/questions
In /api/users,
const express=require('express')
const router=express.Router()
//this handles route: /users
router.get('/',(req,res)=>{})
//this handles route: /users/profile
router.get('/profile',(req,res){})
//this is to retrieve profile of specific user by user id :/users/profile/:userID
router.get('/profile/:userId',(req,res))
router.post('/profile',(req,res))
.
.
Then, in your index.js or entry point of your project,
const users=require('./api/users')
const questions=require('./api/questions')
app=require('express')
app.use('/users',users)
app.use('/questions',questions)
So in effect, you say for any /users route, refer to the users.js file, for any /questions routes, refer questions.js file and so on
Use a simple module, don't be invoking routes until you form a heap.
Try Route Magic
You want to do just 2 lines of code and have the module read your directory and file structure to handle all the app.use routing invocations, like this:
const magic = require('express-routemagic')
magic.use(app, __dirname, '[your route directory]')
For those you want to handle manually, just don't use pass the directory to Magic.

How to declare a mandatory parameter in node.js/express router

i got a series of routes like :
var router = express().Router;
router.get('/',middleware1,middleware2);
router.get('/courses',middleware1,middleware2,..)
router.post('/dates',middleware1,middleware2,..)
app.use('/u',router);
Now, when i declare the root of the route in
app.use('/u',router);
i would like to put a mandatory parameter that has to be present in every route of the micro-app like
app.use('/u/:user_name/',router);
So that i can check for every route with router.param the existence of this param in a db.
Is something like that possible by defining the param in the root of the micro app and not in every single route like
router.get('/:user_name/',middleware1,middleware2);
router.get('/:user_name/courses',middleware1,middleware2,..)
router.post('/:user_name/dates',middleware1,middleware2,..)
as they are actually a lot in the real application and it would be a pain to change them all?
Really thanks !
You should be able to do this using mergeParams so long as you're using a recent enough version of Express.
var router = express.Router({mergeParams: true});
Then this will work:
app.use('/u/:user_name/', router);
mergeParams is documented here:
https://expressjs.com/en/4x/api.html#express.router

Resources