How to implement custom endpoints in a URL shortener slug - node.js

Of all the URL shorteners I have tried, no one allows you to add / is a slug but Rebrandly. E.g: let's say bit.ly/abc, this works, but bit.ly/abc/xyz, this doesn't. But in Rebrandly, rebrand.ly/abc/xyz & rebrand.ly/abc, both work. I tried implementing it on my own, but since we have to use params (:slug), when I visit domain.com/abc/xyz, it says cannot get domain.com/abc/xyz even though, the slug is registered in the DB.
EDIT:
router.get('/:id', async (req, res) => {
try {
const ogDetails = await viewOg(req.params.id)
if (ogDetails === undefined) res.redirect('/')
else if (ogDetails) res.render('view', { og: ogDetails })
} catch (error) {
console.error(error, 'error')
}
})
Here, if the :id, matches something like abc, it works as expected. But let's say it has something like abc/xyz, then I get cannot get path /abc/xyz even though the data is there.

As pointed out by #Evert, using the req.path instead of req.params is the way to go. It will give you the entire path entered.

Related

How do I create dynamic user profile pages using params on expressjs like website.com/#username

This is what I have, my problem is other pages like website.com/settings don't render anymore
router.get('/:#username',(req, res)) => {
res.render('profile')
}
You might not use #
However you can access it with req.params
router.get('/:username',(req, res)) => {
console.log(req.params.username)
res.render('profile')
}
You can access it with: localhost:3000/myname it should now log myname

How to get to a route if theres a route/params in the same level

so I tried to get to stud_router.post(/students/add ) ,but I get directed to stud_router.get(/students/:email ) instead, I want to go /students/add but it sends me to /students/:email instead of /students/add.
stud_router.post(`/students/add` , (req,res,next) => {
res.render(`studentadd/students_add`)
})
stud_router.get(`/students/:email` , (req,res ,next) => {
const found = data.some( stud => stud.email === req.params.email)
if (found) {
res.send(data.filter( stud => stud.email === req.params.email ))
} else {
res.status(400).send({ msg: `no member with the email of ${req.params.email}`})
}
})
There's a problem in your question. These two routes:
stud_router.post(`/students/add`, ...)
stud_router.get(`/students/:email` , ...)
Do not overlap in any way because one is for a GET request and one is for a POST request. An incoming request will only match one of these requests. So, if something is going to stud_router.get('/students/:email' , ...), then it must be GET and it would never match the other route.
So, it appears that perhaps the client is confused about whether it's sending a POST or a GET. Or, your route definitions are confused about what they intend to match.

why am I getting favicon.ico when i am using findOne method for express params routes?

when i am using list.save() method a object other than customList name which is favicon.ico is also
saving as record in following cod, Why am i gatting favicon.ico as object.
app.get('/:listRoute',function (req,res) {
const customList=(req.params.listRoute);
List.findOne({name:customList }, function (err,result) {
if (!err) {
if (!result) {
const list=new List({
name: customList,
items: defaultItems
})
list.save();
} else {
console.log(result);
res.render('list', {
listTitle: result.name,
latestItems: result.items})
}
}
});
})
When you visit a website (any URL on that website), a browser will typically also send a request to that same domain for /favicon.ico so see if the web site offers an icon to be a visual representation of the site.
Since you are using a wildcarded top level route:
app.get('/:listRoute', ...)
That will get hit by the request for /favicon.ico. Some other urls you also may need to watch out for being requested are: /robots.txt, /humans.txt, /sitemap.xml, /ads.txt.
There are a number of ways to work around this:
Your wildcard route can first check req.url or req.params.listRoute to see if it's something it should ignore.
You can place other top level routes that you want to keep out of your wildcard route in a position before this route so they don't end up in this one.
Don't use a top level wildcard route. Instead, use something like /list/:listRoute so it won't automatically match any top level http request. Your use of a top level wildcarded route interferes with other future uses of your site and can create backwards compatibility going forward when you want to add other top level routes to your site. Imagine if sometime in the future, you want to add /contact or /login or /logout. Those all conflict with /:listRoute.
Try to add a callback function to the list.save();
Let me know if this works. The reason is maybe because of sync issues. eg: time taken by mongoDB to update the first document & save > the time taken by the 'Get' method to redirect to itself. Therefore by adding this callback it kinda make sure the code gets saved first and err checked before the redirect.
eg:
list.save(function(err){
if(!err) {
console.log("list is successfully saved"); //log is optional
res.redirect("/" + listRoute);
}
});
When fetching route data using params with express,the entered data easily we can log.But if not adding top-level route and just trying to get the required route eg:
app.get("/:requireddata",function(req,res){
const data = req.params.requireddata;
});
in this case, when loading main page the favicon.ico will generate as a result.
So for getting an exact result, that's when only loading requireddata route we can get the result by using higher level route.
In case there is no higher-level route add just an additional route before requireddata as shown below:
app.get("/add/:requireddata",function(){});
Here /add/ is an additional route for avoiding favicon.ico
For me this worked, so if this information is useful just go head.
Hey there I also came across this exact issue.
So here is my solution to that.
Just enclose everything in a if block and there you go. DONE !!!!
app.get("/:name", function (req, res) {
if (req.params.name != "favicon.ico") {
const name = _.capitalize(req.params.name);
List.findOne({ name: name }, (err, foundList) => {
if (!err) {
//new list with default items created
if (!foundList) {
const list = new List({
name: name,
items: defaultItems,
});
list.save();
res.redirect("/" + name);
} else {
res.render("list", {
listTitle: foundList.name,
newListItem: foundList.items,
});
}
}
});
}
});
P.s.:- It will throw some error from mongo but that'll not affect the overall working.
Hope this helps.

getting parameter from default route

I am getting paramter from default route like this
conf.js: router.use('/' ,require('./hello'));
hello.js:
router.get('/:param', async (req,res)=>{
try{
let { param }= req.params;
console.log(param)
let param = await Params.findOne({param});
if(!param){
return res.redirect('/not-found')
}
})
and when i visit to any url that i am handling or not handling (like this: router.use('/blah',require('./blah'));)
the code above handles it, so how can i prevent that and let the actual handler handle it? for example when i am redirected to /not-found this is actual /not-found handler
router.get('/', async (req,res)=>{
try{
res.render('notfound')
}catch(err){
return res.redirect(url.format({
pathname: '/error',
query: {
message: err.message
}
}))
}
})
but it is handled as /:param
Express routes and middleware are order-dependent. Everying you add to app via use, get, post, etc. is actually one long chain of possible matches for the URL.
If the first route you add matches everything (at least, everything with only one path component) then sure enough, that route is going to match everything.
The solution is to add more specific routes like app.get('/people') first, and catch-alls like app.get('/:param') last.
As for the "not found" case however, I do not understand why you would expect this to fire at all. Of course any one-path-component path will match the /:param route. So perhaps what you really want is to match only certain values of param. For that, Express supports regex rules. There is a good explanation here.
if I understand you , you need to set this route /:param at the end of your routers chain
router.use('/blah',require('./blah'))
//then
router.get('/:param', async (req,res)=>{
try{
let { param }= req.params;
console.log(param)
let param = await Params.findOne({param});
if(!param){
return res.redirect('/not-found')
}
})
so your request will arrive on the first route if it doesn't match it will go to the second one

keystonejs redirect after signin not working with function as second parameter

Use case
I have some custom form for the frontend, that needs a signed in user.
Therefore I use keystone.js' own "requireUser" middleware.
After the user has signed in, to access the site, the default behaviour is to show the admin UI at "/keystone" - however I want to redirect back to the previous frontend page, so the user can start filling in the form.
What works
In keystone.js, in the file with the exact same name, you can set
keystone.set('signin redirect', '/');
which redirects to the home page after the signin.
What I need
However, what I need is to set the url to redirect to dynamically, but when I do this, the function is never called:
keystone.set('signin redirect', function(req, res){
var url = req.session.returnTo || '/';
res.redirect(url);
});
The req.session.returnTo parameter is set in the "middleware.js" file previously mentioned, by taking the req.path property from the frontend page:
exports.requireUser = function (req, res, next) {
if (!req.user) {
req.flash('error', 'Please sign in to access this page.');
req.session.returnTo = req.path;
res.redirect('/keystone/signin');
} else {
next();
}
};
My temporary workaround
In keystone/admin/server/routes/signin.js I extend the redirect property of the locals object from
redirect: keystone.get('signin redirect'),
to
redirect: (typeof keystone.get('signin redirect') === 'string') ?
keystone.get('signin redirect') : keystone.get('signin redirect')(req, res),
I am aware this is not an ideal solution, but since I am still learning about keystone.js, do you know a better way of handling this?
Notice: the same redirect strategy works when setting the "signout redirect" key (to both a string or a function as second parameter) so I guess this must be something that was forgotten to implement?
Thanks
#hajn
Following #1489 issue on Keystone project, there is no ideal solution as of now. Documentation didn't provide the function params properly (I'm considering that as an issue). So, I've reopened the issue 3 days back.
Change routes/middleware.js replacing the method exports.requireUser as follows:
/**
Prevents people from accessing protected pages when they're not signed in
*/
exports.requireUser = function (req, res, next) {
if (!req.user) {
req.flash('error', 'Please sign in to access this page.');
if(req.url) {
res.redirect('/keystone/signin?from='+req.url);
} else {
res.redirect('/keystone/signin');
}
} else {
next();
}
};
From any URL pointing to /keystone/signin, include a from parameter in the query string, as so:
Log in
or
Log in

Resources