Run a function inside Express route and still render page in sendFile - node.js

I have a React app using server-side rendering and I'm trying to hit a route using middleware to check assign redirects.
const app = express();
...
app.use("/", redirectRouter);
app.use("/widgets", widgetRouter);
app.use("/api", apiRouter);
app.use((req: Request, res: Response) => {
res.sendFile(path.join(__dirname, "public", "index.html"));
});
The second two middlewares are for some backend purposes and the last app.use serves up the React app if no other route is found. app.use("/", redirectRouter) is where I'm trying to run a loop that assigns redirects to an array of URLs. I want express to hit the middleware, run, but still serve index.html at the bottom.
Currently app.use("/", redirectRouter) does nothing. I've tried changing "/" to "*" which hits the route but no longer renders the page. I've also tried variations of app.get, app.all, and app.options which also do nothing.
EDIT: Forgot to add that this is inside the redirectRouter
redirectRouter.get("*", async (req: Request, res: Response) => {
// my function
});
But the problems are the same... "*" hangs up the app and "/" does nothing.

Related

NodeJS Express Angular protect Frontend by middleware

I have a NodeJS Backend running, which uses the express framework.
In the frontend I use Angular. In the past I build two Docker Containers (one frontend, one backend). Now I want to build just one container, in which NodeJS run as usual. But it should also provide my frontend. It is working, so here is my code:
import express = require("express");
import session = require('express-session');
import dotenv from 'dotenv';
dotenv.config();
const app = express();
const port = process.env.PORT || 80;
...
const isOnline: RequestHandler = (req: express.Request, res: express.Response, next: express.NextFunction) => {
if (!req.session.user) {
return res.redirect('/auth/login?rd=' + req.originalUrl);
}
next();
};
...
app.use('/api', isOnline, apiRoutes);
app.use('/auth', authRoutes);
app.get('*.*', express.static(path.join('./', 'dist'), {maxAge: '1y'}));
app.get('*', isOnline, (req: express.Request, res: express.Response) => {
res.status(200).sendFile(`/`, {root: path.join('./', 'dist')});
});
app.listen(port, () => console.log('API running on port ' + port));
Code explaination
So actually there are three types of routes. The API, the Authorization and the Frontend paths. The API and the Frontend should be only accessible by users which are authorized. The Authorization is available for everyone.
Issue / Question
The issue is, that I provide all files from the dist folder (in which my build angular app is located), which has a dot (so all file names).
And when I open the app (example.com), I will directly redirect to the auth route. This means the middleware is working. But if add the index file to the path (example.com/index.html), I have access to the app directly, because it is provided by the . route.
Solution Ideas
Is there maybe any way to exclude the index.html from the wildcard statement (or do I stop providing angular by this)?
Can I catch the route exmpla.com/index.html and redirect to the base path?
Or is there any other way to protect my whole angular app by my middleware?
you might completely close public (dist) folder:
app.get('*.*', isOnline, express.static(path.join('./', 'dist'), {maxAge: '1y'}));
leaving publicly open only api and login route
and on the login route in authRoutes, render a login page with some minimal inlined css and form or javascript to handle the login:
authRoutes.get('login', /*res.sendFile with styles and login form*/)
authRoutes.post('login', /*handle login*/)
//...

Does res.send() in an Express JS handler automatically call next()?

I understand that one can call next after res.send in an ExpressJS handler, but does res.send 'automagically' call next in any case?
I have the following code
const express = require('express');
var app = express();
app.get('/', (req, res, next) => {
console.log('in route handler')
res.send('Hello World')
});
app.use((req,res, next) => {
console.log('in middleware')
console.log('...........')
})
app.listen(process.env.PORT || 8080)
My console log is
in route handler
in middleware
...........
If I do indeed call next explicitly after res.send I get
in route handler
in middleware
...........
in middleware
...........
and thus it would seem the middleware is being called twice.
Why is this? Is it because the middleware is also called 'directly' in some fashion, regardless of the route? That is, it is simply always called, even when it is after the route handlers? But I thought if it was after the route handlers, to reach the middleware the route handler preceding it has to call next, as here https://derickbailey.com/2016/05/09/in-what-order-does-my-express-js-middleware-execute/, where it says "It turns out the order in which you add the middleware is important. And since the 2nd 'use' method is added after the 'get' handler, it is never called. The 'get' handler short-circuits the middleware when it renders the page, preventing any further middleware from being processed."
Express version 4.16.0, Node version 11.2.0
Thanks for any clarification!
Why is this?
It's because browsers send an additional request to get favicon; When you go to localhost:8080 chrome ( or firefox ) sends a get request to / hence your server matches this route and logs:
in route handler
Immediately after that it sends a second get request to /favicon.ico but your server does not match any route. it continues its way to middlewares mounted after routing and so logs:
in middleware
...........
Of course by calling next() you've called your middleware explicitly after two above request and so:
in route handler
in middleware
...........
in middleware
...........
But I thought if it was after the route handlers, to reach the
middleware the route handler preceding it has to call next
Of course you are right. Add serve-favicon middleware to your app and your custom middleware never get called without calling next() explicitly unless none of the routes does not get matched:
const express = require('express');
var favicon = require('serve-favicon')
var path = require('path')
var app = express()
app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')))
app.get('/', (req, res, next) => {
console.log('in route handler')
res.send('Hello World')
});
app.use((req,res, next) => {
console.log('in middleware')
console.log('...........')
})
app.listen(process.env.PORT || 8080)
By the way this middleware mounted after all routes is proper place for handling 404's because if we get to this point, none of our apps routes got matched.
app.use()
Middleware allows you to have set of actions which your routes should follow. Consider as if all your routes will do some processing before actually performing designated action for that route.
When I ran your code it printed below and Hello World was rendered in both Mozilla(Version 63.0.3 (64-bit)) and Chrome(Version 71.0.3578.80)
in route handler in middleware ...........
res.send()
Now coming to your question, no there is no need to call next() after res.send() is called. Because as soon as it is encountered, it'll send the response immediately. And yes you are correct the order of middleware does matter. So when you added next() after res.send() following actions were performed:
First the '/' route will return Hello World on the browser stop loading
Your middleware will be called twice, once due to next() and 2nd time due to middleware itself.

when to use app.use() in Node express app

I was trying to comprehend when do we need to use app.use in our node Express
While searching on web, I stumbled on this answer on reddit stating the difference between app.get and app.use
Based on which, I was able to summarise the following things.
app.use act as a super route or middleware? meaning that it gets called on every route written below/after app.use?
Also, would appreciate if someone could add more information/practise about app.use.
When using ExpressJS with NodeJS you can use app.get and app.use for several useful aspects.
After initializing your App like let app = express();, you can find below some examples:
app.use(...)
As you correctly pointed, it is useful for "middlewares", it will apply to all the GETs, POSTs, etc. you indicate afterwords. For example, you can use a Middleware only before the GETs you want to be "with user/pass authentication".
Indicate the folder for static contents: app.use(express.static(__dirname + "/public"));
Including a parser for JSON contents: app.use(bodyParser.json());
Define the "Cookie Parser" signing string: app.use(cookieParser("Signing text example"));
Separate Routers for your URLs in different files: app.use("/api", apiRouter); or app.use("/news", newsRouter); or app.use("/", siteRouter);
For a custom error handler: app.use(sites404handler); or app.use(globalErrorHandler);
app.get(...)
When talking about app.get(...) you are indicating which URLs will be visited via a GET method. But you can use several options here:
Indicate you have a home page: app.get("/", function(req, res) { res.send("Hello world!"); });
Accept POST requests: app.post("/", function(req, res) { res.send("Hello world! With POST call."); });
You can also separate it in another file as "apiRouter.js" and include there: let router = express.Router(); router.route("/books").get(function(req, res) { res.send("/api/books/ called via a Router"); });
app.set(...)
Remember that you also have the option app.set(...). This is useful for example to define View Engines like Handlebars (.hbs files).
Hope this can help!
Use for static path
//Set static path
app.use(express.static(__dirname + '/public'));
use as router
//user
app.use('/', require('./controllers/user'));
use for handline middleware
//Body-parser
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}));// Body parser use JSON data
Use for custom middleware
// force https
app.use ( (req, res, next) =>{
if (req.secure) {
// request was via https, so do no special handling
next();
} else {
// request was via http, so redirect to https
res.redirect('https://' + req.headers.host + req.url);
}
});
app.get route handler is applied to GET requests, either for specified paths or all paths:
Routes HTTP GET requests to the specified path with the specified callback functions.
app.use middleware is applied to all requests, either for specified paths or all paths:
Mounts the specified middleware function or functions at the specified path: the middleware function is executed when the base of the requested path matches path.
use is used to apply some logic (middleware) to specific route or entire application, regardless of request method.

Webpack proxy messing up my routing?

So I'm using webpack for a project on 8080 with a backend on 3000. The proxy seems to work fine, as I can send requests to the backend and access it without issue. However. I need to include this middleware that allows me to have a user load the page, and if they've logged in within a certain amount of time, the initial request they send to the server logs them in automatically.
router.use(function (req, res, next) {
//check token for routes beneath vvvv
})
router.post('/preauth', function (req, res) {
//return user account info if req.token is valid
})
When I try to get to prauth, or even any route before that from the page loaded on 8080 I only touch the middleware and nothing else.
When I do npm run build then try it again from the identical page on 3000, it works as expected.
No, CORS is not enabled and the proxy does not rewrite any url.
Does anyone know if something in my Webpack config might be causing this?
You need install Cors in nodejs:npm install cors, you can try the following below or you see: Nodejs + Vuejs
var express = require('express')
var cors = require('cors')
var app = express()
app.use(cors())
app.get('/products/:id', function (req, res, next) {
res.json({msg: 'This is CORS-enabled for all origins!'})
})
app.listen(80, function () {
console.log('This is a CORS-enabled web server listening on port 80')
})

Angular and NodeJS GET request url with params returns only response object no component HTML

I have a URL like for example 'localhost:3000/verify/a5d5sd', that I have sent to a user's email, upon clicking this link, I am using the param (a5d5sd) in the link for checking some stuff in the server and database(MongoDB) and returning a response with an object, now when the link is clicked or opened on a new tab, I only see the response from the server but not with the HTML from my Angular 2 component.
In my Angular 2 route config module, I did config a route like
{
path: 'verify/:id',
component: MyComponent
},
And on ngOnInit of this component, I am calling a service method that makes a GET http request with a URL like verify/:id the param(:id) I get it using the ActivatedRoute from the #angular/router so that I can make the request.
To handle this I was advised to have the following code for my express to use:
app.use(express.static(path.join(__dirname, 'client/dist')));
app.get('*', function(req, res) {
res.sendFile(__dirname + '/client/dist/index.html');
})
Have even tried with:
app.all('*', function(req, res) {
res.sendFile(__dirname + '/client/dist/index.html');
})
This code works for direct navigation(pasting URLon the search bar) and refreshes for only POST and PUT requests but not working with GET requestS that expects a response from the server side, when I directly visit a URL or refresh the page, the DOM is written with the server response but with not HTML, any assistance will be appreciated.
After define all routes, add the route for the index.html file
// Set your routes here
app.use('/api', api);
// Catch all other routes and return the index file
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, 'path/to/index.html')); //path from the root dir
});
This was work for me.
Thanks

Resources