How to send GET/POST requests using express and react router? - express

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

Related

Unmatched requests entered in the URL don't hit the backend

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.

Shopify - Get shop domain inside a app

I'm new to Shopify app developing and I'm using Node,Express for the back-end and react with polaris libaray.
My question is how to get the shop's domain the request is initiating throug h the app. When I searched I could only found one used in Ruby ShopifyAPI::Shop.current and I'm looking for the similar thing to use in node?
For examples check out https://github.com/BKnights/kotn-shopify-utils
Yes it uses a session.
The code is pretty idiosyncratic. I published it mostly as an easy way to share between my own projects but it's worked pretty well for those.
If you use this where you may scale your servers you'd need to replace the session engine with something more distributed. Cookie sessions work.
This expects to route the setup page of the app to /preferences. Look at that route with the validSession, session, middleware
For passing the domain to Polaris I put the shop info into a plain JS object on the containing page (it's a dustjs template):
<script type="text/javascript">
var KotN = {
shop:'{shop}',
apiKey: '{apiKey}',
shopOrigin: 'https://{shop}.myshopify.com',
locale:'{locale}' || (navigator.languages ? (navigator.language || navigator.languages[0]) : (navigator.userLanguage || navigator.browerLanguage))
};
</script>
and then the Polaris component looks like:
import * as React from 'react';
import {EmbeddedApp} from '#shopify/polaris/embedded';
import ShopProvider from './stores/ShopProvider';
import Status from './views/status';
const shop = KotN.shop;
const shopOrigin = KotN.shopOrigin;
const apiKey = KotN.apiKey;
console.log('shop: '+ shop +' and origin: '+ shopOrigin);
export default class MyApp extends React.Component {
render() {
return (
<EmbeddedApp
apiKey={apiKey}
shopOrigin={shopOrigin}
forceRedirect={true}
debug={true}
>
<ShopProvider>
<Status />
</ShopProvider>
</EmbeddedApp>
);
}
}
I know this is a old thread but i've stumbled here while i was searching for the answer and just wanted to share my solution with you guys that also have the same problem. This is how i'm getting shop domain.
let shopDomain = new URL(window.location).searchParams.get("shop");

react router server side rendering: How to get route params on server side?

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.

Authentication workflow in react/redux

I am trying to setup a new front-end project using react, react-router and redux and I'm stumbling on setting up a login workflow that works well and with many pages.
Here is the logic that I want to have:
Have a variable in the redux store that says if I'm logged in or not. To simplify let's say that we have logged: true/false.
When logged is set to true from the login page, redirect to the page we were redirected from, or redirect to / if not defined. The previous location is stored in nextPathname.
When logged is set to false in a component that requires auth, redirect to /.
My main problem with this workflow is that I want to dispatch an action (set the logged value) and then do a redirection.
Here are a few alternatives that I have explored:
Check if logged==true in my login component's componentWillReceiveProps and redirect if it's the case. This works well for the login workflow but if I have to implement such a logic for the logout component, I have to do it for each component that requires auth. (Mixins don't work thanks to ES6 react components...).
Handle the redirections in a middleware: when the action is about login, run the action and then redirect. However I don't know if this is a best practice or not.
So my question is: how do I make the auth work without too much overhead on all pages that are restricted?
A bit of code:
app.js:
function requireAuth(nextState, replace) {
if(store.getState().Login.logged === false) {
replace({
pathname: "/login",
state: { nextPathname: nextState.location.pathname }
});
}
}
const history = syncHistoryWithStore(browserHistory, store);
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
<Route path="/" component={Archives} onEnter={requireAuth}></Route>
<Route path="/login" component={LoginComponent}></Route>
</Router>
</Provider>,
document.getElementById("app")
);
The LoginComponent:
class LoginComponent extends React.Component<LoginProps, any> {
componentWillReceiveProps(newProps) {
if(newProps.logged) {
let nextPath = "/";
const {location, history} = newProps;
if (location.state && location.state.nextPathname) {
nextPath = location.state.nextPathname;
}
history.pushState({}, nextPath);
}
}
render() {
return <div>This is a login form</div>
}
}
The way you'll want to approach this, I believe, is by using a 'RequireAuth' higher order component to wrap your components that the user needs to be authenticated to access. Here's an example of such, just pay attention to the auth components in the client folder: https://github.com/joshuaslate/mern-starter

Accessing navigator in Actions with React-Native and Redux

Using React-Native (0.19) and Redux, I'm able to navigate from scene to scene in React Components like so:
this.props.navigator.push({
title: "Registrations",
component: RegistrationContainer
});
Additionally I'd like to be able push components to the navigator from anywhere in the app (reducers and/or actions).
Example Flow:
User fills out form and presses Submit
We dispatch the form data to an action
The action sets state that it has started to send data across the wire
The action fetches the data
When complete, action dispatches that the submission has ended
Action navigates to the new data recently created
Problems I'm seeing with my approach:
The navigator is in the props, not the state. In the reducer, I do not have access to the props
I need to pass navigator into any action that needs it.
I feel like I'm missing something slightly simple on how to access Navigator from actions without sending in as a parameter.
In my opinion the best way to handle the navigation with Redux is with react-native-router-flux, because it can delegate all the navigation logic to Redux:
You can change the route from the reducer;
You can connect the router to the store and dispatch its own actions that will inform the store about route changes (BEFORE_ROUTE, AFTER_ROUTE, AFTER_POP, BEFORE_POP, AFTER_DISMISS, BEFORE_DISMISS);
An example
Here is an example on how easily you can save the currently focused route in the store and handle it in a component (that will be aware of being focused):
1. Connect a <Route> to Redux
Connecting a <Route> to Redux is easy, instead of:
<Route name="register" component={RegisterScreen} title="Register" />
you might write:
<Route name="register" component={connect(selectFnForRegister)(RegisterScreen)} title="Register" />
You can also simply connect the component itself in its own file like you usually do.
2. Connect a <Router> to Redux
If you need to inform Redux of the navigation status (i.e. when you pop a route) just override the <Router> component included in react-native-router-flux with a connected one:
import ReactNativeRouter, { Actions, Router } from 'react-native-router-flux'
const Router = connect()(ReactNativeRouter.Router)
Now when you use a <Router> it will be connected to the store and will trigger the following actions:
Actions.BEFORE_ROUTE
Actions.AFTER_ROUTE
Actions.AFTER_POP
Actions.BEFORE_POP
Actions.AFTER_DISMISS
Actions.BEFORE_DISMISS
Take a look at this for an example.
3. Catch the interested actions in your reducer
For this example I have a global reducer (where I keep the information needed by all my app) where I set the currentRoute:
case Actions.AFTER_ROUTE:
case Actions.AFTER_POP:
return state.set('currentRoute', action.name)
Now the reducer will catch every route change and update global.currentRoute with the currently focused route.
You also can do many other interesting things from here, like saving an history of the navigation itself in an array!
4. Update your component on focus
I'm doing it on componentDidUpdate of my component of the route named payment.
If global.currentRoute is payment and the previous global.currentRoute was different, than the component has just been focused.
componentDidUpdate(prevProps) {
const prevRoute = prevProps.global.currentRoute
const currentRoute = this.props.global.currentRoute
if (currentRoute === 'payment' && prevRoute !== currentRoute) {
this.props.actions.doSomething()
}
}
P.S.: Remember to check currentRoute === 'payment', otherwise you'll start doSomething() on every route change!
Also, take a look a the README.md for learning other stuff about the integration with Redux.
Hope it helps, long live Redux!
Maybe you could pass the information about title and component in an action and the component with the navigator can then push the right scene with the information of the state.