Express.js - Allow infinite subroutes, but at least one - javascript

I want to allow any subroute after /example/....
Example of valid URLs:
/example/one
/example/one/two
/example/one/two/three
But doing this:
server.get('/example/*', (req, res) => {
// ...
})
Is also allowing /example without any subroute. And I want to avoid this. How can I do it? Is there any way without using regex?
Thank you so much!

You could simply define two routes - one for /example and another for all /example-subroutes, you need to make sure the order is correct though:
app.get('/example', (req, res) => {
// render default page
});
app.get('/example/*', (req, res) => {
// do other stuff
});

Related

Issue with multiple routes in one file - only two out of three work, the last one won't work

I'm working on making an API using express and MySQL. I'm having issues with my routes. I have it in a separate folder, requiring the different controller files and exporting the router at the end. Here's the issue. The last router.get function will not work. I can swap it and whatever is last will not work. I'll get back an empty array. I'm at a loss as to what can be wrong. Here's my code, the routes file:
const express = require('express');
const router = express.Router();
const getEmployeesController = require('../controllers/getEmployees');
const getEmployeesByIdController = require('../controllers/getEmployeesById');
const getEmployeesByFirstNameController = require('../controllers/getEmployeesByFirstName');
router.get('/', getEmployeesController.getEmployees);
router.get('/:id', getEmployeesByIdController.getEmployeesById);
router.get('/:first_name', getEmployeesByFirstNameController.getEmployeesByFirstName);
module.exports = router;
The 'first_name' router worked when it was second, after the '/', but now it won't. Same with the 'id', worked when its second, but not when it's third.
Here's the controller function, one as an example:
const mysql = require('mysql')
const pool = require('../mysql/connection')
const { handleSQLError } = require('../mysql/error')
const getEmployeesById = (req, res) => {
let sql = "SELECT ?? FROM ?? WHERE ?? = ?"
sql = mysql.format(sql, ['*', 'employees', 'emp_no', req.params.id])
pool.query(sql, (err, rows) => {
if (err) return handleSQLError(res, err)
return res.json(rows);
})
}
module.exports = { getEmployeesById };
/:first_name and /:id match the exact same URLs so only the first one you register is going to get all the matching URLs. They both match /anything.
You really can't define routes like that. There's no way Express knows which route handler you want to use with /anything is the requested URL.
Instead, you need to define a route structure where those two types of URLs are different and you can design a route handler that will uniquely catch each one. I personally don't ever use top level wildcard routes like this because they match every top level URL and they prohibit you using top level URLs for any other purpose in your site.
Instead, you might change your URL design to do this:
router.get('/id/:id', ...)
router.get('/firstname/:firstname', ...);
Then, it would be completely clear from the URL what type of resource was being requested and each route would match only the appropriate URLs.

Two page templates that both render dynamically but share the same route

I have two custom page types that use dynamic routing, queried from Prismic CMS, service_page and about_page. I'm running into a problem in my express server custom routing however - if my servicePage route is listed first, the aboutPage wont render. But if its vice versa, the servicePage won't route but my aboutPages will. Is there a way to have two custom types with dynamic routes? I understand I can add a prefix to them (services/:uid or about/:uid) but I'd really rather avoid that for company purposes.
server.get('/:uid', (req, res) => {
const nextJsPage = '/servicePage';
const queryParams = { uid: req.params.uid };
app.render(req, res, nextJsPage, queryParams);
});
server.get('/:uid', (req, res) => {
const nextJsPage = '/aboutPage';
const queryParams = { uid: req.params.uid };
app.render(req, res, nextJsPage, queryParams);
});
What is your purpose?
When using routing in general the first listed url that matches the url you requested will be displayed. You can't expect any other behavior.
I don't think you can use Regex here if there is a difference between the ids (such as 9 digits vs. 2 digits for example), but you can try searching about it. Otherwise, you must use a prefix.

Route parameters in Express JS

I am using Express JS to handle a route http://localhost:3000/location which allows a mix of parameters and fixed endpoints. For example:
http://localhost:3000/location is the root for the route, which renders a view for a list of locations.
http://localhost:3000/location/map renders a view for a list of the locations drawn on a web map.
http://localhost:3000/location/:id contains a parameter for the ID of a location given in the URL and when called, renders a view for the details of the given location that comes from a database query.
'use strict';
var path = require('path');
var express = require('express');
var router = express.Router();
/* GET route root page. */
router.get('/', function(req, res, next) {
// DO SOMETHING
});
/* GET the map page */
router.get('/map', function(req, res, next) {
// DO SOMETHING
});
/* GET individual location. */
router.get('/:id', function(req, res, next) {
// DO SOMETHING
});
module.exports = router;
Is this a best practise for handling a route with mixed fixed values and parameterized parameters?
More specifically, how to properly handle the problem that when I called "http://localhost:3000/location/SOMETHINGWRONG", for example, http://localhost:3000/location/:id was triggered which led to a database query error because "SOMETHINGWRONG" was not an integer and could not pass?
You can restrict a rule with regex in your route, for example, if you only expect to receive whole numbers, you can use something like this:
router.get('/:id(\\d{12})', (req, res) => {
//...
});
Enter the method if you meet the rule, where "id" is a number with 12 characters
Validate only numbers:
app.get('/:id(\\d+)', function (req, res){
//...
});
To have more control over the exact string that can be matched by a route parameter, you can append a regular expression in parentheses (()).
ex: Your Id is an integer with a maximum length of 10 characters
/* GET individual location. */
router.get('/:id([0-9]{1,10})', function(req, res, next) {
// DO SOMETHING
});

Error with get request for users

When using a get request for all the users in my database (see code below) i only get the "first" user in the database. If i instead try to use the "findOne"-method i get the same user as before no matter what i put in (the username doesn't even have to be in the db it still gives me the same user). I've been trying to understand why this isn't working but can't find any problems with the code. Could it be a problem with db settings or something similar? All help is appreciated!
In AuthController:
// Get all users
AuthController.allusers = function(req, res) {
User.find({}, function(err, users) {
}).then(function(users) {
res.json({users: users});
});
}
In routes:
// GET Routes.
router.get('/users', AuthController.allusers);
Since you are using Sequelizejs, you might want to do findAll.
AuthController.allusers = function(req, res) {
User.findAll().then(function (users) {
res.send({users: users});
}
}
According to the docs:
find - Search for one specific element in the database
findAll - Search for multiple elements in the database

Node.js same uri but different parameters

I'm doing a simple library API with Node.js and express. To fetch a specific book with ISBN or books within a specific genre, I have the following functions:
/api/books/isbn
router.get('/:isbn', function (req, res)
{
var isbn = req.params.isbn
})
/api/books/query
router.get('/:genre', function (req, res)
{
var genre = req.params.isbn
})
So first of all, this will not work because it will only go to the first function regardless of what the input is. Another thing I tried and that worked, was to make only one function that extracts the query parameters:
router.get('/', function (req, res)
{
var isbn = req.query.isbn
var genre = req.query.genre
})
Using this approach I could get the different values by doing calls like /api/books?genre=Adventure or /api/books?isbn=1231230912
But I feel it would make more sense to do it the first way, i.e /api/books/adventure or /api/books/isbn.
So, which way is the 'right' way? And if it's the first one, how can I design the functions so that they can extract the right values?
I think the second approach you tried should also be ok.
However, if you asked me to do this. I will create below apis:
/api/books/genre/Adventure
/api/books/isbn/1231230912
This helps to have clear distinctions between the apis and makes them maintainable and readable for future.
To save the time of writing 2 route handlers, you can write only one route handler function and pass either genre or isbn as parameter.
Try using one route, and by trying to parse the parameter to a number you can check if it's an isbn or genre.
If NaN then genre else isbn
You could do like so :
// /api/
router.get('/isbn/:isbn', function (req, res)
{
var isbn = req.params.isbn
})
// /api/books/query
router.get('/genre/:genre', function (req, res)
{
var genre = req.params.genre
})
This will work and this is the most used format.
And if you wan't to have only one uri then do like so :
// /api/books/isbn
router.get('/', function (req, res)
{
var isbn = req.query.isbn
var genre = req.query.genre
if (isbn)
res.redirect('/isbn/' + isbn);
else if (genre)
res.redirect('/genre/' + genre);
})

Categories