Match API and isomorphic routes - node.js

I have an isomorphic app running on express:
app.get('*', (req, res) => {
match(
{ routes, location: req.url },
(error, redirectLocation, renderProps) => { ... }
Currently I'm matching on the wildcard as this is the approach I've seen taken in most boilerplates and tutorials I've seen.
This issue is now that I need to be able to match specific routes such as /auth and /api which need to be handled differently to the routes for the isomorphic app. i.e. the /auth route needs to authenticate a user.
I need a way to be able to specify those routes but then still maintain the wildcard matching for the routes used my the isomorphic app.
I've considered that instead of using a wildcard matcher I could collect all of the routes for the isomorphic app in to an array and provide that to express instead:
const isoRoutes = ['/home', '/about'];
app.get([isoRoutes], (req, res))
I'm not sure if there's a better way to achieve this though?

If your /auth and /api routes aren't serving pages from your app (i.e. you're using them for AJAX calls), you should have separate request handlers for them.
As long as you specify the non-wildcard routes ahead of the wildcard get, you should be fine with the wildcard.
app.get('/auth', handleAuth);
app.get('/api', handleAPI);
app.get('*', handleRender);

You can code like this:-
req.url.match(//regular expression);
Match() only works with a regular expression.

Related

Express doesn't load page only return json

The problem appears when after reloading a page, server return only json instead that page
I'm using React and return static files from build folder, there is also express handles routing, it is reproduced only in production mode when running localhost everything ok
app.use('/auth', authRoutes);
app.use('/user', userRoutes);
app.use(['/dota2', '/csgo', '/lol'], generalRoutes);
if (process.env.REACT_APP_ENV === 'production') {
console.log('Production is running');
app.use('/', express.static(path.join(__dirname, '../build')));
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, '../build', 'index.html'));
});
}
There are routes
const router = Router();
router.get('/live', liveMatches);
router.get('/upcoming', upcomingMatches);
router.get('/past', pastMatches);
router.get('/:matchId', getMatchById);
router.get('/opponents/:tournamentId', opponents);
router.post('/past-team-matches', pastTeamMatches);
You can visit mySite and you will see json as result but if you clear matchId in URL and click on any match the page will load as normal
There is also react-router
<ServiceRoute
key={key}
path={['/dota2', '/csgo', '/lol']}
exact
access
component={Matches}
/>
<ServiceRoute
key={key}
path={['/dota2/:matchId', '/csgo/:matchId', '/lol/:matchId']}
exact
access
component={MatchInfo}
/>
Let's follow the route matching that express does in this case:
When looking for /dota2/566624, it will match here: app.use(['/dota2', '/csgo', '/lol'], generalRoutes); and return the JSON.
When looking for /dota2, it won't match app.use(['/dota2', '/csgo', '/lol'], generalRoutes); so it will continue down until it matches app.get('*', (req, res) => {, serving the React page.
The problem I see here, is that you're using the very same routes for the API and the front-end routing on React. Ideally, when serving the API and the front-end application from the same server you should prefix the API routes, that way they won't clash with the front-end routes. Let's say that you prefix the API routes with: /api. Now you we have:
app.use('/api/auth', authRoutes);
app.use('/api/user', userRoutes);
app.use(['/api/dota2', '/api/csgo', '/api/lol'], generalRoutes);
// ... everything is the same down here
/api/dota2/566624. Will match against the endpoint that returns the JSON on the first load.
/dota2/566624. Will not match the JSON endpoint and will follow down to the '*' route that loads the React app, later, once the app is loaded that route will be handled by the router you're using on the react application.
Don't add the prefix as I did in the example above, use the const router = Router(); you've been using for this purpose as well. I hard-coded the prefix so that I can keep the example short.
One last question is: Why is this not happening on development?
I don't have all the information but for what I can see you're serving the front-end application from a different server than the one running the API; in that case you won't have the route clashing, since they are server from different ports, e.g front-end served on localhost:8080 and API served on localhost:5000.
I hope this helps! please comment if you need info!

NodeJs/Express: Authorise all endpoints except one

In my NodeJs/express based application, I am authorizing calls to all the endpoints by using the following middleware.
app.use(restrictByCookieMiddleware);
I want to authorize all endpoints except one i.e. I don't want "restrictByCookieMiddleware" middleware to run for "/metrics" endpoint. Is there a way to escape one endpoint?
Here, I found some examples that matches endpoint for which middleware should run, I am looking for a solution that skips one.
Your have a couple of choices:
First, you can just define the one exception route handler BEFORE the middleware. Then, it will handle that route and the routing will never get to the middleware.
app.get("/login", (req, res) => {
// handle that one special route here
});
// all other routes will get this middleware
app.use(restrictByCookieMiddleware);
Second, you can make a wrapper for the middleware that compares to the one specific route and skips the middleware if it's that route:
app.use((req, res, next) => {
// shortcircuit the /login path so it doesn't call the middleware
if (req.path === "/login") {
next();
} else {
restrictByCookieMiddleware(req, res, next);
}
});
// then, somewhere else in your code would be the /login route
app.get("/login", ...);
Third, if you have multiple routes that you want to skip the middleware for, you can segment things by router. Create a router for the non-middleware routes and put all of them on that router. Hook that router into the app object first.
Then, create a second router that contains the middleware and has all your other routes on it.
Place that specific route, you want to exclude, before this line:
app.use(restrictByCookieMiddleware);
So this will solve your problem.

Express JS match route without handling

Given an instance of a Express JS app or router, is it possible to match a request against the apps configured routes and receive a object that describes the route as registered with the app?
For instance, if a request for /users/1 were to be handled by the application, would it be possible for the app/router instance to programatically check if the app has a route that would satisfy this request given the URI and HTTP method?
Desirable sudo(ish) code:
const app = express();
app.use((req, res, next) => {
const handler = app.match(req);
// {
// 'method': 'GET',
// 'path': '/user/:id', <--- mainly looking for this
// 'handler': <function reference>
// }
next();
});
app.get('/user/:id', (req, res, next) => {
// fetch the user and do something with it
});
...
AFAIK there are no publicly documented Express router endpoints that provide the behavior you are describing based on its 4.x Documentation.
However, you could implement this yourself by creating a custom regular expression validator to check if the req.path string matches any defined path. The downside to this is that you would have to maintain that list separately from what is registered to Express, which might prove to be difficult to maintain.
You may be able to root through the internals of the app object to get the functionality you need, but note the instability of that approach will mean your solution could potentially be broken by non-major updates to Express.

Node dynamic routing when route is undefined

I am new with node and am trying to figure out dynamic routing. I have routes already set up such as
app.route('/users')
.get(dbController.collect)
.post(dbController.insert);
app.route('/users/:userId')
.get(dbController.read)
.put(dbController.update)
.delete(dbController.delete);
I want to be able to do something similar for routes that I have not defined in my code while still letting the routes I have defined work as usual. For example if someone was to send a get request to https://example.com/books/12 it would dynamically run the read function as it does for users.
Express routing allows you to do catch-all routes:
app.all('*', (req, res) => {
console.log('Route is unknown')
// Do something here...
})

express.js - use middleware for non static urls only

How do i create middleware that runs on all urls that arent serving static files?
i was thinking of checking if req.url doesnt start with "/js/", "/css/" or "/images/" but there must be a better way
The easiest (and fastest) way is to put your static file middleware ahead of your 'catch all' middleware:
app.use(express.static(STATIC_DIR_HERE));
app.use(function notStatic(req, res, next) {
// everything here will be non-static routes
});
(where app is your express instance)

Resources