Get selected url segment within Express - express

Is there a standard/predefined approach within Express to determine which url segment was used?
const countryCodes = ['us', 'uk', 'ie', 'au', 'ca'];
const cc = countryCodes.join('|'); // supported country codes
app.get(`/${cc}/verify`, (req, res, next) => {
// how to get specified cc value, from "us|uk|ie|au|ca" segment?
});
I can, of course, solve it with a head-on approach, by parsing the url, but I thought Express might have a more civilized approach to this already in place, I just cannot find it.

Related

How to increase the range of an array taken from an API query

I have a function for when clicking a button increase the contents of a list.
Content is removed from an API by the following code:
const [data, setData] = useState();
const [maxRange, setMaxRange] = useState(2);
const getAPIinfo = ()=>{
GetEvents(maxRange, 0).then((response) => response.json())
.then(result_events => {
const events = result_events;
setData({events:events});
}).catch(e => setData({events:events}));
}
And my function is this:
const buttonLoadMore = ({data,type}) =>{
setMaxRange(prevRange => prevRange + 4);
data = data.slice(0,maxRange);
}
what I'm not able to do is update the maxRange value of the API query to increase the list...
this function should be heavily refactored:
const buttonLoadMore = ({data,type}) =>{
setMaxRange(prevRange => prevRange + 4);
data = data.slice(0,maxRange);
}
when you use maxRange here, you are setting new state, while the function itself ir running, the state is not instantly updated, buttonLoadMore is a function in a particular time. it cannot get new maxRange instantly, while running buttonLoadMore does that make sense? Also you cannot update data state just like a regular variable by assigning new variable using = operator, you should refactor this function to something like this:
const buttonLoadMore = ({data})=> {
const newMaxRange = maxRange + 4;
setMaxRange(newMaxRange);
const newData = {events: [...data.events.slice(0, newMaxRange)]};
setData({...newData})
}
also you will get bug here. since your getAPIinfo is setting data state to an object {events: events}. I took the liberty and tried refactoring it here.
There is also a bug in your getAPIinfo in line }).catch(e => setData({events:events})); the events variable you declared in .then function cannot be reached here. It is simply out of scope. unless you know that .catch resolves into data, you will get an error in this line.
take a look at this example here:
const promiseFunction = ()=>{
return new Promise<string>((resolve)=>resolve('i like coca cola'))
}
const getter = () => {
promiseFunction()
.then(response => {
const thenVariable = response;
console.log(thenVariable) // i like coca cola
})
.catch(error=>{
console.log(thenVariable) // Error:Cannot find name 'thenVariable'.
})
}
as you can see .catch() is in different scope than .then() will not be available outside so events cannot be reached by .catch function.
Usually you would use catch for error handling. Maybe show a line on screen, that error has accoured, and data cannot be fetched at this time. etc. There's a very good book that explains all these concepts in detail here: https://github.com/getify/You-Dont-Know-JS
I would strongly recommend for you to switch to typescript because your code is crawling with bugs that should be easily avoided just by type checking, and adding eslint configurations.

API multiple requests with axios / Vue.js, advice on doing things the smarter way

First of: I'm a beginner at Vue.js/APIs so I hope my question is not too stupid (I may not be seeing the obvious) :)
So,
Using Vue.js I'm connecting to this API and want to track the history of each crypto-currencies (no issues with getting any data from the API).
Currencies information are accessible using a URL :
https://api.coinranking.com/v2/coins
And history is accessible using another :
https://api.coinranking.com/v2/coin/ID_OF_THE_COIN/history
As you can see the second url needs the id of the specific currency which is available in the first one.
I would like to find a way to make only 1 get request for all currencies and their history rather than having to make as many requests as available currencies there are (about 50 on this API), I've tried several things but none has worked yet (for instance using the coin url and storing ids of the currencies in a table then using the history url and modifying it with the ids of the table but hit a wall) .
Here's the axios get request I have for the moment for a single currency:
const proxyurl = "https://cors-anywhere.herokuapp.com/"
const coins_url = "https://api.coinranking.com/v2/coins"
const history_url = "https://api.coinranking.com/v2/coin/Qwsogvtv82FCd/history"
//COINS DATA
axios
.get(proxyurl + coins_url, {
reqHeaders
})
.then((reponseCoins) => {
// console.log(reponseCoins.data)
this.crypto = reponseCoins.data.data.coins;
})
.catch((error) => {
console.error(error)
})
//GET ALL COINS UUIDs
axios
.get(proxyurl + coins_url, {
reqHeaders
})
.then((reponseUuid) => {
this.cryptoUuidList = reponseUuid.data.data.coins;
//access to each crypto uuid:
this.cryptoUuidList.forEach(coinUuid => {
console.log("id is: " + coinUuid.uuid)
//adding uuids to table:
this.coinsUuids.push(coinUuid.uuid);
});
})
.catch((error) => {
console.error(error)
})
// COIN HISTORY/EVOLUTION COMPARISON
axios
.get(proxyurl + history_url, {
reqHeaders
})
.then((reponseHistory) => {
//get data from last element
const history = reponseHistory.data.data.history
this.lastItem = history[history.length-1]
// console.log(this.lastItem)
this.lastEvol = this.lastItem.price
// console.log(this.lastEvol)
//get data from previous element:
this.previousItem = history[history.length-2]
this.previousEvol = this.previousItem.price
})
.catch((error) => {
console.error(error)
})
I probably forgot to give some info so let me know and will gladly share if I can
cheers,
I took a look at the API, they do not seem to give a way for you to get everything you need in one request so you will have to get each coin history separately.
However, I do se a sparkline key in the returned data, with what seems to be a few of the latest prices.
I do not know your projects's specifics but maybe you could use that for your initial screen (for example a coins list), and only fetch the full history from the API when someone clicks to see the details of a coin.

NextJS: How to handle multiple dynamic routes at the root

Goal: I would like to achieve github style routing, where abcd in github.com/abcd could resolve to a user profile page or a team page.
I currently have a version that sort of works (see below). Unfortunately I am occasionally getting a white page flash when navigating between 2 dynamic routes.
My server file looks like:
const express = require('express');
const next = require('next');
const { parse } = require('url');
const resolveRoute = require('./resolveRoute');
const port = parseInt(process.env.PORT, 10) || 3000;
const dev = process.env.NODE_ENV !== 'production';
const nextApp = next({
dev,
});
const nextHandle = nextApp.getRequestHandler();
const STATIC_ROUTES = [
'/about',
'/news',
'/static',
];
const DYNAMIC_ROUTE_MAP = {
user: '/[user]',
team: '/teams/[team]',
};
nextApp.prepare().then(() => {
const server = express();
server.get('*', async (req, res) => {
// pass through next routes
if (req.url.indexOf('/_next') === 0) {
return nextHandle(req, res);
}
// pass through static routes
if (
req.url === '/' ||
STATIC_ROUTES.map(route => req.url.indexOf(route) === 0).reduce(
(prev, curr) => prev || curr,
)
) {
return nextHandle(req, res);
}
// try to resolve the route
// if successful resolves to an object:
// { type: 'user' | 'team' }
const resolvedRoute = await resolveRoute(req.url);
if (!resolvedRoute || !resolvedRoute.type) {
console.error('🛑 Unable to resolve route...');
return nextHandle(req, res);
}
// set query
const { pathname } = parse(req.url);
const paths = pathname.split('/').filter(path => path.length > 0);
const query = {
[resolvedRoute.type]: paths.length > 0 ? paths[0] : null,
};
// render route
return nextApp.render(
req,
res,
DYNAMIC_ROUTE_MAP[resolvedRoute.type],
query,
);
});
server.listen(port, err => {
if (err) throw err;
console.log(`🌎 Ready on http://localhost:${port}`);
});
});
I'm wondering if there is a better way to handle this or if I need to move away from NextJS.
Next.JS has built in dynamic routing, which shouldn't require you to create a custom server.js file. If you want full compatibility with Next.JS you should use it's dynamic routing instead.
To create a dynamic route in Next.JS you can create pages with names surrounded in square brackets e.g. /pages/[username].js. This will match all routes on your base domain, so you can set up the example you mentioned with github e.g. http://yourwebsite.com/csbarnes and http://yourwebsite.com/anotherusername.
In the example above you can grab the username in your Next.JS page from the query parameter in getInitialProps just in the same way as you would with any query string parameters:
static getInitialProps({query}) {
console.log(query.username); // the param name is the part in [] in your filename
return {query}; // you can now access this as this.props.query in your page
}
Next.JS always matches static routes before dynamic routes meaning your /pages/ directory can look like this:
pages/index.js -> (will match http://yourwebsite.com)
pages/about.js -> (will match http://yourwebsite.com/about)
pages/contact.js -> (will match http://yourwebsite.com/contact)
pages/[username].js -> (will match http://yourwebsite.com/[anything_else])
Multiple segments
You can have multiple segment dynamic routes, such as http://website.com/[username]/[repo] using folders in your pages directory:
pages/[username].js -> (matches http://yourwebsite.com/[username])
pages/[username]/[repo] -> (matches http://yourwebsite.com/[username]/[repo])
In this instance your query object will contain 2 params: { username: ..., repo: ...}.
Route "prefixes"
You can have multiple dynamic routes with different "prefixes" if you wish by creating folders in your pages directory. Here is an example folder structure with a website.com/[username] route and a website.com/teams/[team] route:
Dynamic number of different segments
You can also have dynamic routes with any number of dynamic segments. To do this you need to use an ellipsis ("...") in your dynamic route file name:
/pages/[...userDetails].js -> (will match http://website.com/[username]/[repo]/[etc]/[etc]/[forever])
In this instance your this.props.userDetails variable will return an array rather than a string.
One addition regarding usage of SSR and SSG pages and you need to differentiate those with dynamic URLs by adding the '-ssr' prefix to an URL.
For example, you need some pages to be SSR, then you can create under the pages an ssr folder where you could put the page [[...path]].js with getServerSideProps. Then you could use such rewrite in the next.config.js under async rewrites() {:
{
source: '/:path*/:key-ssr', destination: '/ssr/:path*/:key-ssr'
}
that covers such URLs:
/page-ssr
/en/page1/page-ssr
/en/page1/page2/page-ssr
/en/page1/page2/page3/page-ssr
etc.
You can't have two different types of dynamic routes at one route. The browser has no way of differentiating between a and b, or in your case a username and a team name.
What you can do is have subroutes, lets say /users and /teams, that have their own respective dynamic routes.
The folder structure for that in NextJS would look like this:
/pages/users/[name].js
/pages/teams/[name].js

NodeJS Express Moongose route regex [duplicate]

This question inspired by this post but in my case I need to filter MongoId. Is it possible to make filtering easily that the below because I need use it in each route?
app.post('/:mongoId(^[0-9a-fA-F]{24}$)', function(req, res){
// Send query based on mongoId
}
You're almost there, just don't add the ^ and $ anchors. And the uppercase A-F range isn't even necessary since Express seems to match case-insensitive:
app.post('/:mongoId([0-9a-f]{24})', function(req, res){
var id = req.param('mongoId');
...
});
According to the Express API documentation, yes, you can use a regular expression as a path:
Regular expressions may also be used, and can be useful if you have
very specific restraints.
app.get(/^\/commits\/(\w+)(?:\.\.(\w+))?$/, function(req, res){
var from = req.params[0];
var to = req.params[1] || 'HEAD';
res.send('commit range ' + from + '..' + to);
});

vars in express routes are blocking any following routes

I'm trying to use vars in my Express routes. They work fine but after i use a variable in a route, any routes after that one will not work. Here's really simple example.
/////////////////////////////////////////////planets
router.get('/:planetID', function(req, res, next) {
if(req.params.planetID == "hoth"){
res.render('index', {
title: 'Hoth',
subtitle:"Damn its cold"
});
}
});
////////////////////////////////////////////////jedi
router.get('/jedi', function(req, res, next) {
res.render('characters', {
title: 'Jedi',
subtitle:"why the f is this happening?",
});
});
In this example, the jedi route doesnt render anything. But if i put the jedi route before the planet route, everything works fine. Has anyone encountered this before?
Many thanks in advance for any help.
Express routes work in a pipe, that means that it will first check the first route and it will see that it accepts a parameter and it cannot see any difference between "/23423423" and '/jedi'. It assumes that jedi is an ID aswell. you should prefix it with
/planet/:planetID
for them not to conflict each other.
Express uses path-to-regexp for matching the route paths; see its documentation for all the possibilities in defining route paths. Apart from that, when you are working in express middle wares, do take care of the order of middle wares defined.
Note : Router is also a middleware in Express.
So you are working with two routes :
/:planetId
/jedi
If you define route /:planetId at the top then it actually treats /jedi as the same route with value of param planetId = jedi, whereas if you place your second route i.e.; /jedi at the top then it goes for exact match condition and if it find /jedi then only it executes the corresponding action else it will try with other routes defined.
ahh ok scopsy, now I see how your /23423423 example applies. Im sorry guys, I didnt post enough info I'm realizing. I had to use an if() statement to tell the route to move on if it didnt find a match. I did this using the next() function and a counter var.
router.get('/:planetID', function(req, res, next) {
var counter = 0;
planetJSON.planets.forEach(function(item){
counter++;
if(item.link == req.params.planetID){
planetDisplay.name = item.name;
planetDisplay.text = item.text;
planetDisplay.image = item.image;
res.render('index', {
planetGrab:planetDisplay,
planetList:planetJSON
});
}//end if()
else if(counter + 1 > planetJSON.planets.length){
next();
}
});//end for each()
});//end router.get
thanks again for your help