Adding named parameters in express route api - node.js

I have an express route that looks like this:
app.get('/api/v1/username/:option', function(req, res) {
// do stuff
})
How can I modify this route so that the URL show the parameter name of option (option=)? For example:
http://localhost:8080/api/v1/johndoe/option=my-cool-option

That's a URL segment, not a parameter.
If you want it like you've shown the URL, it'd be
http://localhost:8080/api/v1/johndoe/?option=my-cool-option
Note the question mark ?, this specifies that it's a GET parameter.
app.get('/api/v1/:username', function(req, res) {
//req.params.username would equal 'johndoe'
//req.query.option would equal 'my-cool-option'
})

Related

Support for a URL where the question mark to denote the start of queryparams is url encoded

I've got a Node/express application where our users can be directed to the application from Google Ads. The problem here is that they encode all "special characters" of the URL (Google have said this themselves).
I have an endpoint that looks like this:
app.get("/", (req, res) => {
res.send("Hello World!");
});
And I make a request to GET myurl.com/?foo=bar, my response will be what you'd expect ("Hello World!"). I can also easily pull the queryparams out of the request object.
However, if I have a campaign set up in Google Ads with some query parameters being tacked onto the end of the URL (i.e. UTM params), the request generated might look like GET myurl.com/%3Ffoo%3Dbar. Express out of the box doesn't automatically decode those characters, so when that URL gets hit, the express application will just return a 404 because there isn't an endpoint handler for /%3Ffoo%3Dbar.
What are my options here? Do I really have to write middleware to deal with this? I could use something like:
app.use(function(req, res, next) {
req.url = req.url.replace(<insert some regex here>, function() {
<insert some logic here to do the url decoding>
});
next();
});
But it feels weird to have to do this.

Handle POST request from multiple routes all at once

I am submitting a simple contact form in my website's footer (in footer.pug):
form(method="POST" action="contact_form")
input(type='email' name='ct_email' data-name='ct_email' required)
textarea(type='text' name='ct_message' data-name='ct_message' required)
button(type='submit') Send
Since the form is in a template, and the footer template is used throughout the site, the form can be submitted from various routes:
/contact_form
/route1/contact_form
/route1/de/contact_form
and so on...
So now it seems I have to create a handler for all the possible routes:
router.post('/contact_form', function(req, res, next) {
// ...
}
router.post('/route1/contact_form', function(req, res, next) {
// ...
}
How can I easily handle POST requests from all the routes they may be coming from without writing a handler for each?
You can use absolute path reference in your form and it will always submit to the same route even though the form is in different pages.
Try this
form(method="POST" action="/contact_form")
Notice the action changed from contact_form to /contact_form. When you add /, you start referencing the path as an absolute path to the domain. So now, from all pages, the form will be submitted to http://your-domain/contact-form.
Not entirely sure if this is what you mean, but the first argument to ExpressJS's router (I assume that's what router is doing here) can be an array. So instead of:
router.post('/contact_form', function(req, res, next) {
// ...
}
router.post('/route1/contact_form', function(req, res, next) {
// ...
}
You can just do:
router.post(['/contact_form','route1/contact_form'],function(req,res,next){
//some fancy logic to handle both routes.
})
Of course, this requires that you keep a list of these possible routes. On the other hand, you can follow Dinesh Pandiyan's advice, and just use an absolute path. So instead of page1.html, page2.html, page3.html, etc. all having their own own router (or own entry in your router array), you'd essentially be saying "Go to the domain route, then go to this address".
Each request should be handled in separated functions because each request has its own logic. However if you want
function request(req, res, next) {
// Your logic
}
router.post('/contact_form', request) {
// ...
}
router.post('/route1/contact_form', request) {
// ...
}
Right now, I don't have a way to test this code, but I think that will help you.
Here is yet another potential solution - use an independent function as a route handler.
router.post('/a', handlePost);
router.post('/b', handlePost);
router.post('/c', handlePost);
function handlePost(req, res, next){
// use req.path here to figure out what url was called
}

how to add query parameters to router.get

Environment: MEAN tech stack
Hi, I want to add a query parameter to my router.get but I'm not sure how to define it.
Works like this right now:
http://test.com/path1/path2/1
router.get('/path1/path2/:userId', (req, res) => {
let route = `GET /path1/path2/${req.params.userId}`;
I just want to add a search query parameter, would it be something like this?
http://test.com/path1/path2/1?q=test
And how would that get defined in the router.get?
You do not need to add a query parameter to your route directly. Just keep /path1/path2/:userId.
Within your function you can then check, if a query parameter exists, here via req.query.q.
// http://test.com/path1/path2/1?q=test
router.get('/path1/path2/:userId', (req, res) => {
let route = `GET /path1/path2/${req.params.userId}`;
// If http://test.com/path1/path2/1, req.query.q is undefined
console.log(req.params.userId, req.query.q);
});
You use the req.query object to get query parameters.
So, for the URL http://test.com/path1/path2/1?q=test, you could get the query parameter like this:
router.get('/path1/path2/:userId', (req, res) => {
console.log(req.params.userId); // "1"
console.log(req.query.q); // "test"
});
Doc for req.query is here.
For this url http://test.com/path1/path2/1?q=test
access path params = req.params.userId.
access query params = req.query.q.
Read more from Express documentation
http://expressjs.com/de/api.html#req.query
http://expressjs.com/de/api.html#req.params

Dealing with slash characters in request parameter using Express route

I'm currently working on a URL shortener app using Express.
I want the user to be able to enter a URL like this:
https://www.exampleurlshortener.com/new/https://www.google.com
The problem is whenever I try to specify the parameter using Express it will only extract the 'https:' section and everything after that is lost because the 2 backslashes are registering as a new route:
app.get('/new/:url', (req, res) => {
console.log(req.params.url) // outputs 'https:'
I thought about specifying each section as a new parameter but if inner is blank this ends up throwing a 404. I would need to check if inner is blank using this method otherwise the user would be able to type https:/something/www.google.com
app.get('/new/:prot/:inner/:address', (req, res) => {
// throws 404 on valid addresses
Is there a simple way to solve this that I'm missing? Is the full URL available to be checked somewhere in the request? Or can parameters ignore backslashes?
You can use an expression to for your URL placeholders:
app.get('/new/:url(*)', (req, res) => {
console.log(req.params.url) // will output 'https://www.google.com'

Express.js routing with optional param?

I have two situations to get data from DB
To show normal data
http://exampleapp.com/task/{{taskId}}
To edit data via posting
http://exampleapp.com/task/{{taskId}}/?state={{app.state}}
Both url have the same http://exampleapp.com/task/{{taskId}} just a little bit different with last phrase ?state={{app.state}}
I use Express routing as followed:
app.get('/task/:taskId/(?state=:status(pending|cancel|confirmed|deleted))?', routes.task.show);
But I dont know why it does not work ?
For example error: Cannot GET /task/51d2c53f329b8e0000000001 when going to h**p://exampleapp.com/task/51d2c53f329b8e0000000001
Query strings cannot be defined in routes. You access query string parameters from req.query.
app.get('/task/:taskId', function(req, res) {
if (req.query.state == 'pending') { ... }
});
However, if you're modifying a task, this is not the appropriate way to do it. GET requests SHOULD be idempotent: the request SHOULD NOT modify state. That's what POST requests are for.
app.get('/task/:taskId', function(req, res) {
// show task info based on `req.params.taskId`
});
app.post('/task/:taskId', function(req, res) {
// set task `req.params.taskId` to state `req.body.state`
});
You could either have a <form> that posts to the task, or make an ajax request:
$.post('/task/1', { state: 'pending' }, function() { ... });
According to the Express API, you cannot mix RegExp routes with string routes.
You should do something like this (I'm assuming taskId is an integer):
app.get(/^\/task/([0-9]+)/(?state=:status(pending|cancel|confirmed|deleted))?, routes.task.show);
However, I don't see why you cannot only check if req.query.state is defined in your route. It's probably less error prone and easier:
app.get("/task/:taskId", function( req, res, next ) {
if (req.query.state) {
// Do things
}
next();
});
Your problem is that query strings are not considered in routing. You will either have to redesign your urls (ie, include the state into the url itself, instead of the query string) or check the query string in your route handler function.

Resources