Express server using default route, also serve API - javascript

The way I have this app set up, every request gets serviced by the same route that will do some JS serverside rendering:
server.use("*", (req, res) => {
console.log(`from the server route: ${req.path}`)
const context = {};
const serverRenderedHTML = ReactDOMServer.renderToString(
React.createElement(
StaticRouter,
{ location: req.url, context },
React.createElement(AdminApp)
)
);
if (context.url) {
res.redirect(context.url);
}
res.write(template({ serverRenderedHTML }));
res.end();
});
But, I would also like to use my express server as an api for some resources, routes set up like this:
//server.js
server.use("/api/products", productRoutes);
// products.js
router.get( (req, res) => {
var productQuery = Product.find({})
productQuery.exec(function(err, products){
res.json(products)
})
})
However, I am unable to hit the API, as all of my requests are getting picked up by that server.use function.
Is there any way to make sure that the routes under /api/ namespace are properly picked up? Should I just make a different server for the API?

Apparently, I needed to include an argument for the route. I had to change:
router.get( (req, res) => ...
to
router.get("/", (req, res) => ...

Related

Passport-Local and Express Router Issue with Imports

I have an Express.js app with a Passport-local Auth, I'm tying to implement express router to my app since it's getting really hard to debug
This is an example of my main file (app.js)
const express = require('express')
const app = express()
const passport = require('passport')
const testing = require('./routes/file')
app.use(passport.initialize())
app.use(passport.session())
app.use('/router', testing)
const initializePassport = require('./passport-config');
initializePassport.initialize(passport);
app.get('/', checkAuthenticated, (req, res) => {
let user = data.key
res.send(`Hi ${user}`)
})
app.post('/login', checkNotAuthenticated, passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login',
failureFlash: true
}))
function checkAuthenticated(req, res, next) {
if (req.isAuthenticated()) {
return next()
}
res.redirect('/login')
}
function checkNotAuthenticated(req, res, next) {
if (req.isAuthenticated()) {
return res.redirect('/')
}
next()
}
app.listen(3000)
This is what my main file is like, this works perfectly fine
and just to make things clear, The function are to check if a user is authenticated or not, also my passport-config returns something called data.key which is basically the username of the user.
Now I've created a router here app.use('/router', testing) with the route as /router for testing purposes
Now this is my file.js that is being used by express
const express = require('express'), router = express.Router();
var bodyParser = require('body-parser');
// Handle POST requests
router.use(bodyParser.urlencoded({ extended: false }));
router.use(bodyParser.json());
router.get('/test', checkAuthenticated, (req, res) => {
let user = data.key;
res.send(`Hello ${user}, this is coming from the router`)
})
module.exports = router;
Now as expected when I open the site at localhost:3000/router/test it returns a undefined error for data.key since it's not imported and then when I comment that out the line causing the error then checkAuthenticated doesn't work, even when I'm not logged in I can get access to the page, now this is the problem, how do I fix it, I've gone through the entire express router docs I couldn't find luck there.
Thanks
Your function is not in the global scope you need to set it to global to be able to see it like:
GLOBAL.authFunction = function(){
}
Suggestion:
1- make a module for auth name it authModule.
2- import the module auth check middleware where ever you want.

get express params in an intercepting middleware

I would like to intercept a req.params item in an interception layer:
let's say I have an express server application instance:
const app = express();
with an interception layer:
app.use((req, res, next) => {
console.log(req.params.myParam);
next();
})
and many endpoints like this one:
app.get('/anything/:myParam', (req, res) => res.send('hello'))
this logs 'undefined' which is quite natural because when the interception middleware within the "use" is executed, the param name has not been defined yet. But I really need to known in this interception layer the value of myParam parameter with these constraints:
I known how the parameter is supposed to be named in the intercepted endpoints (myParam)
I can't know how the url is structured in the intercepted endpoints (can be /anything/:myParam, or /:myParam/anything and so on...)
Does anyone know a solution for that case ?
The comment from #indrajaj26 about using app.param() leads me on a working solution.
Here is the way I'm proceeding:
/** express application */
const app = express();
// Interception layer
app.param('myParam', (req, res, next, myParam) => {
/* Store myParam into something reusable (like locals as suggested by #nullromo for instance).
* I'm using zonejs in my case: creating a forked zone with myParam stored in zone properties.
*/
next();
});
// Consuming intercepted data. Anywhere in the callstack of any endpoint using "myParam" as request parameter:
console.log(Zone.current.getZoneWith('myParam')?.get('myParam'));
Thanks everyone for your ideas!
A simple solution would be to pass the middleware function when defining the route.
Example:
function middleware(req, res, next) {
console.log(req.params.myParam);
next();
}
app.get('/path', middleware, function(req, res) {
...
});
Full example:
const express = require('express');
const app = express();
function middleware(req, res, next) {
console.log(req.params.myParam);
next();
}
app.get('/path', middleware, function(req, res) {
...
});
app.listen(3000, () => console.log('server is online'));
You can store the params on the response.locals object for use by later middleware.
const express = require('express');
const app = express();
app.get('/:name', (request, response, next) => {
// generate some kind of result
const result = `Hello, ${request.params.name ?? 'world'}!`;
// save the result and the params onto the request.locals object
response.locals = { params: request.params, result };
// call the next middleware to pass on the params and result
next();
});
app.use((request, response, next) => {
// here you have the params since they were parsed by the previous middleware
console.log(`Intercepted: ${JSON.stringify(response.locals.params)}`);
// now actually send the response that you wanted to send
response.status(200).send(response.locals.result);
});
app.listen(3000, () => {
console.log('listening');
});
This requires storing the request.params on the response.locals object in every handler, which is not the greatest. But it also means that you don't have to specify the interceptor middleware every time, if that's a problem for your case.

Why after sending a response, node is still executing further middleware in Express?

so i have a very basic blog app made in express, and the thing is that when i respond to app.get('/') and render a home page view, the server is still responding to app.get('/:id') that is further in the code.
I didn't call next() in app.get('/'), and from what i know, when i render a view, then the rest of middleware after shouldn't get executed, so why is it executing app.get('/:id')?
const express = require('express');
const mongoose = require('mongoose');
const app = express();
// Setting up basic middleware
app.use(express.static('public'));
app.use(express.urlencoded({ extended: true }));
app.set('view engine', 'ejs');
app.set('views');
// Database things
// ...
// Home page
app.get('/', (req, res) => {
res.render('index')
})
// Get add new blog page
app.get('/add-new', (req, res) => {
res.render('addblog')
})
// Respond with single blog page
app.get('/:id', (req, res) => {
const id = req.params.id;
Blog.findById(id)
.then(data => res.render('blog', { data }))
})
// 404
app.use('/', (req, res) => {
res.render('404')
})
I did not understand properly what you meant to ask but from what I understood, I think you mean to ask that even if someone calls /123, (Here 123 is the id) the code to be executed should be
app.get('/', (req, res) => {
res.render('index')
})
If this is what you mean to ask, then that's not how it's done. Only www.yoursite.com/ request goes to app.get('/', (req, res) and any route having value after www.yoursite.com/ like www.yoursite.com/123 will go to the other route which renders single page. 1 more thing. As you have written that if you didn't call next, rest of the middleware shouldn't execute after render, that is not the case. The entire middleware has to execute unless there is an error or you call the return statement like return res.render or simply return.
your 404 Route should be :
app.get('*', function(req, res){
res.render('404');
});
every middelware you have to return something in you case:
return res.render('the-page')
without return it will not work

Express.js - Access target route details in Application-level Middleware

Background:
I have an express application with some simple routes and Router-level Middleware. I want to register an Application-Level Middleware.
The Problem
In Router-Level Middlewares. I can access req.route object. But I can not access the same object inside an Application-Level Middleware.
I can understand this, as inside Application-Level middlewares the program is not inside the route yet.
But is there any way to get req.route object or something equivalent to req.route.path inside global middlewares?
req.path or req.originalUrl contains the real url not the route path.
Example
const express = require('express');
const app = express();
const port = 3232;
app.use((req, res, next) => {
const route = req.route; // It is an application level middleware, route is null
return next();
});
app.get('/test/:someParams', (req, res) => {
const route = req.route; // can access req.route here because it is a Router-level middleware
console.log(route.path)
console.log(req.path)
return res.send('test')
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
Output
Request: GET#localhost:3232/test/33333
/test/33333 // I don't need this.
/test/:someParams // This is what I want to get inside the Application-Level Middleware
Alternative Solution
An alternative solution to this problem can be as the following
const express = require('express');
const app = express();
const port = 3232;
function globalMiddleware(req, res, next) {
const route = req.route;
console.log(route) // can access it
return next();
}
app.use((req, res, next) => {
const route = req.route; // It is an application level middleware, route is null
return next();
});
app.get('/test/:someParams', globalMiddleware, (req, res) => {
const route = req.route; // can access req.route here because it is a Router-level middleware
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
But injecting the same middleware to each and all of my routes does not sound like a smart solution. Specially on bigger applications.
A dump of router object
{
"path":"/test/:someParams",
"stack":[
{
"name":"globalMiddleware",
"keys":[
],
"regexp":{
"fast_star":false,
"fast_slash":false
},
"method":"get"
},
{
"name":"<anonymous>",
"keys":[
],
"regexp":{
"fast_star":false,
"fast_slash":false
},
"method":"get"
}
],
"methods":{
"get":true
}
}
the path key is the thing I want to get. Please note that req.route.path is not the same as req.path
I was having this difficulty too. I didn't find anything on the internet that would solve it so I tried to search the express code itself for what it did to find the route. Basically it is the same thing as the code below, it looks for which regexp is valid for that route. (Google Translate)
const url = req.originalUrl.split('?')[0] // Routes with query
const layer = req.app._router.stack.find(layer => {
return layer.regexp.exec(url) && layer.route
})
So you can access the original path:
console.log(layer.route.path)
What data do you expect in req.route?
you can use req.url, req.method, req.originalUrl, etc...
or in Application level middleware you can add new field to req object
req.customRoute = {yourField: "yourValue"}
and this field will available in route level middleware
You can use middleware as per request
const middleware = (req, res, next) => {
// Append what you want in req variable
req.route = "abc" // let abc
req.path = "path" // req.route.path
next();
}
You can get it here from middleware
app.get('/test/:someParams', middleware, (req, res) => {
console.log(req.params.someParams)
console.log(req.route) // "abc"
console.log(req.path) // "path"
});
For application level middleware
app.use((req, res, next) => {
req.route = "abc" // let abc
req.path = "path" // or req.route.path
next()
})

node.js & express: internal redirect for request inside of router

I am using current express 4 and node.js 6.x. In my web application I need to do an internal redirect (i.e. redirect from one route handler to another, without sending a redirect to the client). This works within toplevel express or within the same router, but not if the source request is located in a Router.
Please see the following minimal working example:
const express = require("express");
const app = module.exports.app = express();
const router = new express.Router();
app.use("/myrouter", router);
// not working
router.get("/test", (req, res, next) => {
req.url = "/toplevel";
next();
});
// working
app.get("/test", (req, res, next) => {
req.url = "/toplevel";
next();
});
// debug output
app.use((req, res, next) => {
console.log(`request to ${req.url}`);
next();
});
app.get("/toplevel", (req, res) => {
res.end("toplevel");
});
app.listen(8000);
If I open "http://localhost:8000/test", I get the correct output "toplevel", and the debug output prints "request to /toplevel". Unfortunately it doesn't work for "http://localhost:8000/myrouter/test". The debug output prints "request to /myrouter/toplevel" and I get the result of the default express 404 handler.
Is there any may to redirect the request from "/myrouter/test" to "/toplevel" without redirecting the client (i.e. res.redirect())?

Categories