I have an app with frontend and backend. The user can login and sign up and perform CRUD operations. All of the requests defined in the backend are hitting the api. However, some unmatched requests that the user enters in the search bar like: https://myapp.com/foo/bar/foo/bar/ don't hit the backend (I am trying to redirect all such requests by using app.all() by appending it to the end of all the routes in the app). When I enter an unmatched request in the backend api directly, it returns the correct response. SS attached below:
However entering an unmatched from the frontend does nothing. In the network request tab, this is what I see. The request type is document and initiator is other. Also, the request is not logged at the backend. How to solve this issue?
Routes:
import express from 'express'
import { userSignUp, userLogin } from '../controller/userController.js'
const router = express.Router()
router.post('/login', userLogin)
router.post('/signup', userSignUp)
export default router
import express from 'express'
import authorization from '../middlewares/authorization.js'
import {
createTest,
getAllTest,
getSingleTest,
deleteTest,
updateTest,
} from '../controller/workoutController.js'
const router = express.Router()
// router.use(authorization)
//Get everything
router.post('/getAll', getAllTest)
//Get a single workout
router.get('/:id', getSingleTest)
//Post a new workout
router.post('/', createTest)
//Delete a single workout
router.delete('/:id', deleteTest)
//Update a single workout
router.patch('/:id', updateTest)
export default router
server.js file
import dotenv from 'dotenv'
dotenv.config()
import express from 'express'
import morgan from 'morgan'
import workoutRoutes from './router/workout.js'
import userRoutes from './router/user.js'
import mongoose from 'mongoose'
import cors from 'cors'
import multer from 'multer'
import rateLimit from 'express-rate-limit'
import SlowDown from 'express-slow-down'
const app = express()
const upload = multer()
app.use(upload.array())
//get the response in json
app.use(express.json())
//using morgan to log requests
app.use(morgan('dev'))
//using cors to make fetch requests
app.use(cors())
//routes
app.use('/api', userRoutes)
app.use('/api', workoutRoutes)
app.all('*', (req, res) => {
res.status(400).send({ message: 'Invalid Route' })
})
//connect to DB
mongoose
.connect(process.env.MONG_URI)
.then((data) => {
app.listen(process.env.PORT, () => {
console.log('Listening on Port', process.env.PORT)
})
})
.catch((err) => {
console.log(err)
})
Edit 1: Was checking my console.log and saw this console.warn logged for the routes that are unmatched. My best guess now is that react-router is not letting the request hit the api.
SS attached for reference.
I have found the answer and posting this for future geniuses. This goofy question exists because of my lack of understanding of react-router.
I am using react-router and basically inside the browser window all of the routing is handled by the react-router. So, when you hit a path in the address bar, the react router first checks it's routes if that path exists. If it does, the component that exists at that path is rendered (the request is NOT sent to the backend. Requests only reach the backend when a component at that path renders and uses fetch or any other api to get data). Otherwise, it looks for a handler for that path which you can set up using *. Example:
<Routes>
<Route path="/" element={<Homepage />} />
<Route path="/:id" element={<SingleBlog />} />
<Route path="/createBlog" element={<CreateBlog />} />
<Route path="/:id/edit" element={<EditBlogs />} />
<Route path="/signup" element={<SignupForm />} />
<Route path="/login" element={<LoginForm />} />
<Route path="*" element={<Page404 />} />
</Routes>
This path with * will serve every url that the react-router does not recognize(This does not mean you shouldn't have a check in the backend. Imagine a person modifiying the request and sending it to the backend). A request is sent to the backend using fetch or axios or any other api. When a page is rendered, you will probably have set up apis to fetch you data from the backend and if that route doesn't exist in the backend then you need to set-up a handler in the backend to catch it and send a response to the user.
Related
I'm trying to follow this guide to setup testing an express API in Insomnia. I have setup a poretected route at http://localhost:5000/api/v1/private/dashboard with express-openid-connect. I have also setup an machine-to-machine application and API in auth0.
When in Insomina I try to access http://localhost:5000/api/v1/private/dashboard I am redirected to the universal login page. Trying to login does nothing. I don't think I've set this up right, but am strugging to understand the process and can't find a similar issue when searching.
I want to be able to make GET/POST/PUT/DELETE requests in Insomnia and the data be returned the same as unauthenticated routes.
I have tried various configuration changes including using client credentials, in body, using a different app type but I don't really understand auth0's configuration so don't know what I'm doing.
server.js (where the authenication check is called via oidc)
import express from "express";
import oidc from "express-openid-connect";
import publicRoutes from "./routes/publicRoutes.js";
import privateRoutes from "./routes/privateRotues.js";
const app = express();
const {
auth,
requiresAuth,
} = oidc;
const config = {
authRequired: false,
auth0Logout: true,
secret: process.env.SECRET,
baseURL: process.env.BASE_URL,
clientID: process.env.CLIENT_ID,
issuerBaseURL: process.env.ISSUER_BASE_URL,
};
console.log(config);
app.use(auth(config));
app.use("/api/v1/public", publicRoutes);
app.use("/api/v1/private", requiresAuth(), privateRoutes);
app.use("*", (req, res) => res.status(404).json({error: "File not found"}));
export default app;
Hello
I have an express app running as a REST server, meaning all the /api/xx routes are process in express.
I also have a react app (served by express when accessing the /) as a client, which also has a router, the problem comes when i refresh a react route, the 404 page error of express shows up instead of the react page itself.
The react router
<Router history={browserHistory}>
<Route path="/" component={MainLayout} user={user}>
<IndexRoute component={DocumentsPage}></IndexRoute>
<Route path="about" component={AboutPage}></Route>
<Route path=":id/preview" component={PreviewPage}/>
<Route path="upload" component={UploadPage}></Route>
</Route>
</Router>
and when i access with a <Link to="upload" </Link> the page shows up, and when i refresh the page, i got the 404 express's page..
Any help would be lovely <3
You need to configure a fallback, so that every request that doesn't exist in your Express routing is redirected to your React app at /. Here's a basic example of how to do that with Express:
import fallback from 'express-history-api-fallback';
import express from 'express';
import http from 'http';
// Set up express
const app = express();
const server = http.createServer(app);
const root = `${__dirname}`;
// History Fallback for express
app.use(express.static(root));
app.use(fallback('index.html', { root }));
// Listen
server.listen(8080, '0.0.0.0');
Then you can also handle your 404 page with React Router.
OK, I'm still trying to build react.js app with server side rendering. I having big time dealing with react-router with parameters. I cannot extract routes params from route on server side to make proper query on DB.
Here is my react-router route:
import {Router,Route} from "react-router";
import React from "react";
import App from "../components/app";
import {HomeContainer} from "../components/home";
import {TagContainer} from "../components/tag";
export function createRouter(hist) {
const routes = <Route component={App}>
<Route path="/" component={HomeContainer}/>
<Route path="/tag/:unique_name" name="tag" component={TagContainer}/>
</Route>;
return (
<Router history={hist}>{routes}</Router>
);
}
the route run fine until I add parameter ":unique_name" to the route
<Route path="/tag/:unique_name" name="tag" component={TagContainer}/>
on the server side, I cannot extract unique_name from the route to make query on DB:
Here is the route on server(Using Node.js & Express.js):
const router = express.Router();
router.get("/tag/:unique_name", ServerRenderController.tagRender);
server.use(express.static(path.join(__dirname, '..', '/build')));
server.use(router);
and here is my "ServerRenderController.tagRender":
function tagRender(req, res) {
console.log(req.params.unique_name);
/*
output :
mytag_unique_name -> this is the route params
style.css ->stylesheet - how the hell it become route params?
app.js -> client code - how the hell it become route params?
vendor.js -> vendor scripts - how the hell it become route params?
manifest.js -> manifest file -how the hell it become route params?
*/
match({browserHistory,routes, location:req.url}, (err, redirectLocation, renderProps)=> {
if (redirectLocation) {
//#TODO: response redirect location
console.log('redirect location');
}
if (err) {
//#TODO: response error
console.log(err.stack);
}
if (!renderProps) {
//#TODO: route to 404
console.log("no renderProps");
}
renderPage(renderProps); // return HTML to client with __PRELOADED_STATE__
}
Questions :
What did I do wrong in server code (routing, express static
middleware...).
How do I extract correct route params from route? (I only want to extract "mytag_unique_name" as the only params when I browse to http://localhost/tag/mytag_unique_name)
right now the route params including static files that should be
send as MIMETYPE css/js.
OK. Turn out I made mistake in references to style sheets and scripts file.
In the server render code must refer <link href="/style.css" rel="stylesheet"/>
with slash (/) at the begining of the rel attr.
Note here for anyone have same problem.
I currently have express set up to serve a static html page where my react components mount to. I'm also using react router because I have nested routes. For example:
I have an App component (green outline). Within that component, I'm rendering a Header component (orange outline) and a Footer component (red outline) and passing in a Review component (blue outline) through this.props.children.
My server.js file (express):
const express = require('express');
const app = express();
app.use(express.static('dist'));
const PORT = process.env.PORT || 3000;
app.listen(PORT, function() {
console.log(`Listening on port ${PORT}...`);
});
My routes.js file (react-router):
import React from 'react';
import ReactRouter, {
Router,
Route,
browserHistory,
IndexRoute,
} from 'react-router';
import App from '../components/App';
import Home from '../components/Home';
import Review from '../components/Review';
import Game from '../components/Game';
const routes = (
<Router history={browserHistory}>
<Route path="/" component={App} >
<IndexRoute component={Home} />
<Route path="/game/:id" component={Game} />
<Route path="/game/:id/review" component={Review} />
<Route path="*" component={Home} />
</Route>
</Router>
);
export default routes;
My question is, I want to be able to make GET/POST requests (GET from the Game component to display all reviews from a db and POST to create a new review from the Review component), but where should that happen? I can't tell if it has to happen in my express routes because it seems to me that all express is doing is rendering the static html page and then react-router is taking over after that with handling which components to display.
Any and all help is greatly appreciated.
For the GET request, you can load initial data in a separate function, than load that data in after the component has mounted using componentDidMount like so:
class Game extends React.Component {
constructor() {
this.state = { data: [] }
}
loadCommentsFromServer() {
$.ajax({
....
}
});
}
componentDidMount() {
this.loadCommentsFromServer();
setInterval(this.loadCommentsFromServer, this.props.pollInterval);
}
}
You can do simply have another function for the POST.
I just wanna share my experience, hope it helps. I'm never doing a request on React Route although it can. So, I prefer to perform this action inside component itself on componentDidMount() method, in your case it will be on Game component.
My consideration is to make component reusable, especially if the the component is depends on the request. The benefit when you're implementing request inside the component is your component will automatically call for the request when it mount, wherever you're mounting the component on the route path.
Refers to my experience, you can also make a request on express as server side request, because there are particular condition that need to perform a request handling from server side. Such as handling Cross Origin Resource Sharing (CORS) issue when request to public API from client side, authentication request handling like using OAuth, and more.
If you're request is quite simple, I think request inside the component is sufficient.
Hope it helps. Thank you
quick question regarding using React-Router. I'm having trouble getting my server to handle pushState (if this is the correct term). Originally, I was using a module called connect-history-api-fallback, which was a middleware that enabled me to only server up static files form my dist directory. Visiting the client www.example.com obviously worked and I could navigate throughout the site, additionally, refreshing at any route like www.example.com/about - could also work.
However, I recently added one simple API endpoint on my Express server for the React app/client to ping. The problem now is that while I can get the initial page load to work (and thus the /api/news call to work, to fetch data from a remote service), I can no longer do a refresh on any other routes. For example, now going to www.example.com/about will result in a failed GET request for /about. How can I remediate this? Really appreciate the help! PS - not sure if it matters, but I'm considering implementing Server Side Rendering later on.
import express from 'express';
import historyApiFallback from 'connect-history-api-fallback';
import config from '../config';
import chalk from 'chalk';
import fetch from 'node-fetch';
import path from 'path';
const app = express();
// FIXME: Unsure whether or not this can be used.
// app.use(historyApiFallback({
// verbose : true
// }));
//// DEVELOPMENT MODE ONLY - USING EXPRESS + HMR ////
/* Enable webpack middleware for hot module reloading */
if (config.get('globals').__DEV__) {
const webpack = require('webpack');
const webpackConfig = require('../build/webpack/development_hot');
const compiler = webpack(webpackConfig);
app.use(require('./middleware/webpack-dev')({
compiler,
publicPath : webpackConfig.output.publicPath
}));
app.use(require('./middleware/webpack-hmr')({ compiler }));
}
//// PRODUCTION MODE ONLY - EXPRESS SERVER /////
if (config.get('globals').__PROD__) {
app.use(express.static(__dirname + '/dist'));
}
//// API ENDPOINTS FOR ALL ENV ////
app.get('/api/news', function (req, res) {
fetch('http://app-service:5000/news')
.then( response => response.json() )
.then( data => res.send(data) )
.catch( () => res.sendStatus(404) );
});
// Wildcard route set up to capture other requests (currently getting undexpected token '<' error in console)
app.get('*', function (req, res) {
res.sendFile(path.resolve(__dirname, '../dist', 'index.html'));
});
export default app;
Express works by implementing a series of middleware that you "plug in" in order via .use. The cool thing is your routes are also just middlware — so you can separate them out, have them before your history fallback, and then only requests that make it past your routes (e.g., didn't match any routes) will hit the fallback.
Try something like the following:
const app = express();
// ...
var routes = exprss.Router();
routes.get('/api/news', function (req, res) {
fetch('http://app-service:5000/news')
.then( response => response.json() )
.then( data => res.send(data) )
.catch( () => res.sendStatus(404) );
});
app.use(routes);
app.use(historyApiFallback({
verbose : true
}));