Making HTTP-request During React Server-Side Rendering on Electrode Platform
Imagine a React application with SSR where you need to make an external HTTP request and set use its result as initial state. This might be difficult to accomplish as the rendering process is synchronous, so we would have to find a right place to make the request before rendering, and thus postponing rendering and sending the response. In this short article, I will show you how to implement this on Electrode React project.
Electrode is a great platform for building universal React application, but its documentation is quite confusing for React newcomers. I have been trying to find how to dispatch http-requests on server, but spend quite some time on research before finding the right way.
Let’s say we have an API service which authenticates users with a JWT token. We do not want to make additional request on the client to check whether we are currently authenticated or not, so we decided to use cookies as a mean to pass JWT token in the initial request. In this way, we could check the authenticated status and redirect users to sign in page if he or she tried to access a protected route being unauthenticated.
But how do we find the right way to do this? The answer lies in Electrode Redux Router Engine.
According to the documentation, each route definition can have the init
property that references JS module to load server-side redux data. This JS module should export a function–which is also clear from the example code that comes with generated electrode project. The init
function has the options parameter which has Request object available under options.req
. Also, it can return a Promise, which means that we can do any asynchronous operation that we need. So, this is the place!
Here goes the full code example.
In ./src/client/routes.jsx:
const routes = [
{
path: "/",
component: withRouter(Root),
init: "./store-init",
routes: [
// List your routes here.
]
}
];
In ./src/server/routes/store-init.jsx:
import reducers from "../../client/reducers";
import axios from "axios";
const url = "https://example.com/api/auth-check";
export default async function authCheck(options) {
let isAuthenticated = false;
const jwt = options.req.state.jwt; // Request cookies are available in req.data object.
if (jwt) {
try {
const response = await axios.get(url, {headers: {Authorization: `bearer ${jwt}`}});
if (response.data && response.data.isAuthenticated) {
isAuthenticated = true;
}
} catch (e) {
console.log("Something went wrong :(");
}
}
return {
reducer: reducers,
initialState: {
isAuthenticated
}
}
}
You can use different init-files per route, so you could make more specific requests depending on the requested route. If you do so, do not forget to add the top-level awaitInits
property–which is a function that will wait until all child inits are resolved.