Hacks for effective Fullstack development with React and Node
Published at 2023-04-17
Updated at 2023-04-17
Last update over 365 days ago
Licensed under MIT
javascript
nodejs
react
Today I’m going to show you an optimal workflow for effective development with Node.js and React. If you’ve ever worked on a project with multiple package.json files, you might know the pain of juggling multiple terminal tabs, remembering which commands start which server, or handling CORS errors.
Fortunately, there are some tools available that can alleviate some of these headaches.
Monorepo Setup for React and Node
Let’s say we’re working on a monorepo with two package.json
files: one is in a client
directory for a React front-end powered by Create React App, and one is at the root of the repository for a Node back-end that exposes an API that our React app uses. Our React app runs on localhost:3000
and our Node app runs on localhost:8080
. Both apps are started with npm start
Since we have two package.json
files, this means that in order to get our front-end and back-end up and running, we need to make sure we’ve run npm install
and npm start
in both the root directory and the client directory. Here’s how we simplify this.
1. Running two servers at the same time
One improvement we can make to our development workflow is to add a build tool to run multiple npm commands at the same time to save us the trouble of running npm start
in multiple terminal tabs. To do this, we can add an npm package called concurrently to the root of our project.
In the root of our project, we will install it as a development dependency.
npm install -D concurrently
Then, in our root package.json
scripts, we will update our start
script to use them simultaneously.
{
"name": "my-app",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "concurrently --kill-others-on-fail npm run server npm run client",
"server": "node index.js",
"client": "cd client && npm start"
},
"dependencies": {
"express": "^4.17.1"
},
"devDependencies": {
"concurrently": "^6.0.1"
}
}
Now, we have three npm scripts:
npm run server
starts our Node application,npm run client
runsnpm start
in the client directory to start our React application,npm start
runsnpm run server
andnpm run client
at the same time.
2. Installing front-end and back-end dependencies with a single command
Another aspect of our workflow that we can improve is the installation of dependencies. Currently, we need to manually run npm install
for every package.json
file we have when setting up the project. Instead of going through that hassle, we can add a postinstall script to our root package.json
to automatically run npm install
in the client directory after the installation has finished in the root directory.
{
"name": "my-app",
"scripts": {
...,
"postinstall": "cd client && npm install"
},
}
Now, when we install our monorepo, all we need to do to get it up and running is run npm install
and then npm start
at the root of the project. There is no need to enter any other directory to run other commands.
3. Proxy API requests from the backend
As I mentioned earlier, our Node backend exposes the API endpoints that our React app will use. Let’s say our Node app has a /refresh_token
endpoint.
Out of the box, if we tried to send a GET request to http://localhost:8080/refresh_token
from our React app at http://localhost:3000
, we would run into CORS issues. CORS stands for cross-origin resource sharing.
Typically when you encounter CORS errors, it’s because you’re trying to access resources from another domain (i.e., http://localhost:3000
and http://localhost:8080
), and the domain you’re requesting resources from is not allowed.
To tell the development server to proxy any unknown requests to our API server in development, we can set up a proxy in our React app’s package.json
file. In client/package.json
, we’ll add a proxy for http://localhost:8080
(where our Node app is running).
{
"name": "client-app",
"proxy": "http://localhost:8080",
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
...
}
Now, if we restart the server and set a request to our Node app’s /refresh_token
endPoint (without http://localhost:8080
) using fetch(), the CORS error should be resolved.
fetch("/refresh_token")
.then((res) => res.json())
.then((data) => console.log(data))
.catch((err) => console.error(err));
Next time you’re working on a monorepo project like this, try these three tips to streamline your development workflow.
That’s all folks! I hope this helps you become a better dev!
@khriztianmoreno