If no nextjs page use IIS page - iis

If deploying nextjs on IIS is there a way to default back to IIS for routing if there is no nextjs page?
I want to use nextjs for my homepage and wiki etc, but there are other pages routed from IIS directly if there is no page jsx?
Eg)
homepage.com -> loads nextjs page/index.js
homepage.com/wiki/some-page -> loads nextjs page/wiki/[some-page].js
homepage.com/viewer -> loads iis/viewer/index.html

Two options
Front the application with a proxy like nginx and maintain static routing, e.g: /viewer -> /iis/viewer/index.html
Configure your 404 page in nextjs to a redirect. First setup a custom App with next, then in your App getInitialProps which receives a ctx
// Modified from https://github.com/vercel/next.js/discussions/16749#discussioncomment-1604423
if (ctx.res.statusCode === 404) {
res.writeHead(301, { Location: '/iis/'+ctx.asPath })
res.end()
}
Note that appropriate handling when 404 pages are received by IIS would have to be setup on the IIS end.

Related

Deployed React website tries to fetch from server instead of rendering a component

For example, /new works fine with localhost (server using localhost:5000 and client 3000). However, after deployed on Heroku, by clicking the "Add new" link from the Navigation bar (with bootstrap and added as {Link} part), the page is rendered as expected, but if I refresh the page, it shows "cannot get /new" since it is trying to fetch from server (there is no such API from server).
by clicking
by refreshing
The website is deployed by running "npm run build" and building react client into the /public folder of server (Express node.js).
How should I solve this?
When you're refreshing the page the browser sends request to your heroku server to fetch that path.
While you want the browser to reload your React app route.
To fix that you'll need to:
Prepend your api endpoints definition with /api prefix, for instance /api/coaches.
Update your React app code to make api requests to updated enpoints.
Add this code to your express server after your endpoints definitions:
app.get('*', (req, res) => res.sendFile(path.resolve('build', 'index.html'));

Angular Universal app can't make post requests to my Heroku node API

My website is a SPA built with Angular, but it uses SSR with Angular Universal to provide crawlable and social media sharing content.
All GET requests in my server are handled by Universal like this:
app.engine(
'html',
ngExpressEngine({
bootstrap: ServerAppModuleNgFactory,
providers: [provider]
})
)
app.set('view engine', 'html')
app.set('views', __dirname)
app.use('/', express.static('./dist', {index: false}))
app.use('/', expressStaticGzip('./dist', {
enableBrotli: true
}))
app.get('/*', (req, res) => {
res.render('./dist/index', {
req: req,
res: res
})
})
and my pages contents are provided by Angular Services POST requests built with the same queryParams of the requested url.
One example:
If the user visits the url https://mywebsite.com/products?page=1&itemsPerPage=12 (GET request by default), the Angular Universal app and the Angular Router dynamically build my page template and the products list is provided by a Service that triggers a POST request to this URL: https://mywebsite.com/request-products with the following params in body:
{
page: 1,
itemsPerPage: 12
}
Then the Universal App builds the template with some *ngFor directives to populate it before serving it to the client.
This approach makes all my pages visible to webcrawlers and I also get the benefits of a Single Page Application.
When I'm testing my app, I build my Angular app, both Browser and Server builds, and set my environment like this:
export const environment = {
production: true,
apiUrl: 'http://localhost:7070/'
}
and serves my app in localhost, it works perfectly, without errors. My POST requests, like mentioned before, are all handled perfectly. But when I try to set my apiUrl to 'https://mywebsite.com/' and serve my app also in localhost, to access directly my API hosted in Heroku, I just can't access my POST routes.
My node express server app in Heroku is configured to accept requests from other domains, I can access it normally in my localhost server, but when I try to access it through my Angular Universal server build, it just won't work.
I know that I have to use absolute URLS in my Universal Apps, and I'm doing it already, but it's not working.
Does anyone know what I have to do to access external APIs in my Angular Universal Apps via https?
Thanks!
I've found the problem, and it's something really simple.
It turns out that I must use 'www' in my absolute url, like this:
'https://www.mywebsite.com/'
Now everything works perfectly, both from my localhost and my heroku servers.
Thanks to everyone that took some time to read my question!

Node.js / Express.js + Angular router - server overwriting client view with response object when using direct link

I am building a node.js app with express, I am hosting an Angular SPA in the public folder.
The app runs and the hosting works fine when I use the angular router for navigation around the website, but when I directly try to access the link, for example: http://192.168.1.4:3000/posts, the entire body of the website is just the JSON response object, without the app
this is the Node.js code handling the get request
postRouter.route('/')
.options(cors.corsWithOptions, (req, res) => {
res.sendStatus(200);
})
.get(cors.cors, (req, res, next) => {
posts.find({})
.then((post) => {
res.status(200);
res.setHeader('Content-Type', 'application/json')
res.send(post);
}, (err) => next(err))
.catch((err) => next(err));
})
this is my angular service sending out the get request
getPosts(): Observable<Post[]> {
return this.http.get(baseURL + 'posts')
.catch(error => { return this.processHttpService.handleError(error); });
}
Post Component .ts file
ngOnInit() {
this.postService.getPosts()
.subscribe(posts => { this.posts = posts, console.log(this.posts); },
errmess => this.errMess = <any>errmess);
}
Again, when i use my Angular 5 client app hosted in the public folder, built with ng build --prod, the JSON object is retrieved from the mongodb database and is displayed correctly on my website, along with the rest of the app, the header, the body, and the footer.
it might also be worth noting that the console.log on the ngOnInit() is not displayed on the browser when using the direct link.
Any advice/fix is greatly appreciated
You have a clash of routes between angular and your express application. Angular is served up on one route (I'm guessing the / route) and then it sort of "hijacks" the users navigation. It does this by not actually changing web pages, instead it just changes the URL in the navigation bar, but never actually makes a web request to get to that resource.
You've then got endpoints on a web server listening on those endpoints. This means the moment you visit the /posts page, you're not asking angular to do anything. In fact, angular isn't even loaded because that only gets loaded on the / route. Instead you're going straight to your API.
There are ways around this, to start with many people put their API fairly separately, either on a subdomain or mounted on /api (such as /api/posts). Then your angular app can be served up on the / route. There are other techniques you can use to then allow a user to go to /posts and still get your angular app loaded.
You can use a few approaches for this such as the hash location strategy, or you can serve up your angular application from any route on the application (* in express) and load the angular app which will then take over. This second approach is most comment, it usually results in hosting your api on a sub domain and then serving your angular app on the * route of the normal domain name. For example: api.myapp.com will serve only JSON responses, but any route on myapp.com will serve the angular app, such as myapp.com/posts.

How can I redirect a page from an external Node/Express server

I have a React/Node/Express web app up and running with two separate servers. React is running on localhost:8080 and proxying API requests to my node server running on localhost:3000. This will be my setup for production, so if possible, I would like to leave this structure intact.
The problem I'm running into is part of my API needs to redirect the user to a page to grab a token from the Spotify API, and then redirect back to my correct page upon successful authentication.
The API call proxy is being done like this:
const axios = require('axios');
const axiosInstance = axios.create({
baseURL: process.env.NODE === 'production' ? '' : 'http://localhost:3000'
});
module.exports = axiosInstance;
Up to this point, this works great, as I can keep my React and Node server completely separate.
As I mentioned above, I need to redirect my React front-end through an API call on my back-end, to a different page (Spotify authentication) to authenticate users, which then Spotify redirects back to my site after a user allows or disallows access to their Spotify accounts.
On my front-end - localhost:8080, the user clicks a button which calls this function.
authenticate = () => {
axios.get('/login')
.then(data => console.log(data));
}
Which calls this endpoint on localhost:3000.
router.get('/', (req, res) => {
let scopes = 'user-read-private user-read-email';
let client = process.env.SPOTIFY_CLIENT;
let redirect = 'http://localhost:3000/login/redirect';
res.redirect(`https://accounts.spotify.com/authorize?response_type=code'
&client_id=${client}${scopes ? `&scope=${encodeURIComponent(scopes)}` : ''}
&redirect_uri=${encodeURIComponent(redirect)}&state=${state}`);
});
When I click the login button on my front-end, I get this response, which I believe to be from Spotify as I can successfully make cross-origin requests to other endpoints in my API.
Failed to load... No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
However, when I manually navigate to localhost:3000/login, the redirect works and I'm sent to the Spotify authentication page.
In addition, I tested trying to redirect my front-end to any url, and it doesn't work.
So my question is, how can I redirect my front-end through an API call from a server that doesn't also serve my static front-end content?
The reason this isn't working is because your axios.get('/login') is an AJAX request to your server which your server is then telling the AJAX request to be redirected to Spotify, but Spotify doesn't have your front-end server registered so you get the CORS error, since your request was initiated while on the front-end server URL.
Your AJAX request is following that redirect that was given, not trying to load your browser on http://localhost:3000, which would then redirect it to the Spotify authorization page.
Now, rather than doing:
authenticate = () => {
axios.get('/login')
.then(data => console.log(data));
}
You could do:
authenticate = () => {
window.location.href = "http://localhost:3000"
}
This would direct your front-end application to visit the URL of your API server and follow the redirect immediately to Spotify and then you could authorize and be redirected back to your API server, which could then redirect back to your front-end application.
Truthfully, this is a bit overkill, since presumably your React application and node application look like they would be running on the same domain outside of development, given the relative URL's you're using. If you're using create-react-app, you should follow their instructions on configuring your proxy via this or manually via this.
tl;dr you need to tinker with how your proxy is configured or change from doing an AJAX request there to actually going directly to /login (not doing an AJAX request).

React Routing vs Express Routing

Been watching alot of tutorials and i see that there is express routing as well as react routing.
Is the react routing for client and the node js routing for server (api?).
Wanting to know if someone could please clarify this as new to React, Node, Express.
Thanks
It is possible (and even recommended) to use both of them in combination.
TL;DR
react-router is used to navigate between multiples pages/views of your front-end app/website. Usually in a single page app (SPA), where pages/views are loaded dynamically.
express router is a way to return static content (index.html, image.png...) AND to handle API calls that are often related to database logic. Those routes are handled server-side.
Example
myapp.com/my-portfolio is a view and should be handled and rendered by react router
// this router render pages components dynamically based on the url
<Route path="/my-portfolio" component={Portfolio} />
<Route path="/page2" component={Page2} />
myapp.com/user/create or myapp.com/api/getMyJson is an api call that should be handled server-side by express router:
// app.js
// api call that return json data
// this is where I will usually return database content
app.get('/api/getMyJson', (req, res) => {
res.send('{"my_var":"value"}');
});
// api call that return the content of folder app/public where
// the index.html and static resources are usually exposed
app.use(express.static('app/public'))
Single page application workflow
The front-end (client browser) request the back-end (your server) for the application static content (myLogo.png, index.html...) usually served by express router
While the first page is loaded and the user begin to interact with the app, the front-end continues to load other pages in the background (lazy loading)
When the user navigate to another page (with react-router), the page is already loaded and the user is taken there without any further server call nor page reloading
On another hand, express router need to handle API calls like myapp.com/user/userId/get/notifications to get data that is not "static" like json data.
I'll try explain the difference through an example. Say we have a single page application built with react at www.example.com
React Routing
We hit www.example.com and the index.html is loaded from the server. Note that it has all of your react pages in your bundle.js file. You now click the about button on the navbar, this sends you to www.example.com/about. This call does not hit the server, it is handled by your react router.
Express
Much like above we hit www.example.com and get the index. This time when we hit /about we get information from the server
Take a look at this blog post:https://medium.com/airbnb-engineering/isomorphic-javascript-the-future-of-web-apps-10882b7a2ebc

Resources