MERN Full-Stack Tutorial 2020 — Part 3 (connect frontend & backend)

Jin
7 min readSep 24, 2020

Source Code — https://github.com/JinKim7/mern-weather-app

Concurrently Run Frontend and Backend

Within the server app, we need to add more scripts in package.json so we can run both the frontend and backend at the same time.

In the scripts property, we should only have the start command.
Add in these additional commands (server, client, and dev commands):

"scripts": {
"start": "node server/server.js",
"server": "nodemon server/server.js",
"client": "cd client && npm run start",
"dev": "concurrently --kill-others-on-fail \"npm run server\" \"npm run client\""
},
  • npm run server — this command uses nodemon instead of native node to start the server. Improves the dev experience since your app will auto-restart any time there is a change in any of the files.
  • npm run client — instead of going into cd client manually and doing npm run start every time to run the client, this script will help you just stay in one location of your app to run either client or server or both.
  • npm run dev — this will run both client and server at the same time, concurrently.

After adding these commands, run this in your root directory of your app (not inside /client)

npm run dev

This should open a new tab that leads you to your client at localhost:3000
To check your server is working correctly, go to localhost:5000/api/weather

Concurrently running client and server apps
Updated package.json in root

Sweet! We now confirmed that both frontend and backend are running concurrently.

Create a Proxy for Localhost

As of right now, we can’t call the backend endpoints from our frontend. Ideally, on our localhost:3000 client app, we would be able to call our localhost:5000 server app to get weather data. However, it doesn’t work that way out of the box and we will need to proxy our server to call our weather apis (learn more about proxying here).
This will allow our frontend app (client) to call our backend apis (server).

In your /client/package.json file, add

"proxy": "http://localhost:5000/" 
Add Proxy property in client’s package.json
  • this will enable us to call the backend server from our client

Calling Our Server from Client and Save To LocalStorage

The first endpoint we will call is the /api/weather endpoint. This is called when we submit the form to save the zipcode and temp metric.

After the endpoint is called, the form will then save the current weather data at the moment we clicked “save” to our local storage as the property CurrentWeatherData

client/src/Components/WeatherForm.jsx

Don’t just copy/pasta this code directly. There are comments and ellipsis to shorten/ignore the code that should already be in your file. The code you see should mostly be new changes/additions to your current code.

Changes that were made in WeatherForm.jsx:

  • import axios from ‘axios';— to use it to make api calls
  • componentDidMount — this will be triggered anytime the component mounts onto the page (i.e. refreshing the page — more info here)
  • refreshSavedWeather— every time the component mounts, we will call the refreshSavedWeather function which will call our endpoint to get the weather data based on our zipCode and temp metric choice (°C or °F)…and then save it within local storage with CurrentWeatherData as the key.
  • saveFormData — generic function to save form data and currently saves to local storage
  • saveToLocalStorage — to save the zipcode, temp metric, and current weather data to local storage
  • <Form onSubmit={this.saveToLocalStorage}>— when we submit the form onSubmit , it will trigger the function this.saveToLocalStorage to save the info in the form

Let’s test it out.

Try running the app (npm run dev) and saving a zipcode w/ your choice of temp metric.

Checking Local Storage

To look a look at your local storage to make sure it worked:

  • right-click on the app and click Inspect (Ctrl+Shift+I or F12)
  • click the Application tab at the top and then click on Local Storage under the Storage section — you should see your localhost url (http://localhost:3000).
Screenshot of local storage

Depending on your use-case, you can use localStorage to store data that you don’t really need in your database, like mongo.

I chose to add the localStorage aspect in this app bc if I deploy this app, I don’t need to deploy this app w/ the mongo connection and have any risk of security issues. However, I added the mongo aspect to make it a true “MERN” app.

Checkpoint #8https://github.com/JinKim7/mern-weather-app/tree/8.save-to-localstorage
Files Changeshttps://github.com/JinKim7/mern-weather-app/pull/9/files

Save to Mongo instead of Local Storage

client/src/Components/WeatherForm.jsx

Changes that were made in WeatherForm.jsx:

  • saveToMongo — to have an option to save to our mongodb in addition, or replacement, of the local storage saving.
  • Call the saveToMongo method in saveFormData in line 24— if you just want to just use mongodb to save/get data, then you can replace all the instances where you’re saving to local storage and the store w/ one instance of using mongo itself.

When you go through the flow of running the app and saving the form again, the weather data should save in your mongo database now instead of local storage.

Checkpoint #9https://github.com/JinKim7/mern-weather-app/tree/9.save-to-mongo
Files Changeshttps://github.com/JinKim7/mern-weather-app/pull/10/files

Redux

This one is going to be a doozy. I will only gloss over general concepts of Redux, but if you want to learn more about it, read about it here.
Redux will be our tool to help us with state management. Basically, any time the state of our web app changes, the entire application will be able to reflect that w/o a page reload.

There are three key components to know with Redux, imo. That would be Actions, Reducers, and the Store.

Creating our redux actions — create a new folder in client/src/actions an index.js file to host all our actions:

client/src/actions/index.js
  • The action type that were made are SAVE_ZIP, SAVE_WEATHER_DATA, etc… This will describe the actions that we will be taking place with our app and what kind of information will be within our payload.

Reducers

The reducer will do the work to change the application’s state based on which action was called to the redux store.

Create these reducer files in client/src/reducers

  • history.js — this will update the weather history list and only keep the top 10 most recent saves.
  • index.js — accumulates all of the reducers into one file so the components can just import and pick which reducer it wants.
  • temperature.js — saves the temperature metric state into the store so it can be used in any component
  • weather.js — keeps track of the current weather data within the store and pulls from local storage as default.
  • zipCode.js — saves the zipcode to the store so that the current zipcode save is accessible to all components.
Client folder structure so far for redux (actions/reducers)

Integrating the Redux Store

Whenever we first created our client app, we installed redux into our package.json, so now we will start using it.

Starting out with the index.js file within client/src , we will refactor it to include the Redux store and also a chrome extension that’ll help debug anything within the Redux store.

client/src/index.js — the inline comments explains it all..

After this change, you should able to run your local app (npm run dev) and be able to see the Redux DevTools helper when you Inspect the console with the Redux tab now.

Screenshot of Redux DevTools chrome extension in action

Connect Redux into our Components

Now, half of the components need to be refactored to use the redux store.
The redux store will manage the state/props from now on.

client/src/Components
  • WeatherForm.jsx— the changes made was removing the static history state, saves the refreshed weather data into props so that the other components can use it, and mainly adding redux boilerplate.
  • WeatherHistoryPanel.jsx — mostly redux boilerplate additions in this file and replacing this.state.history with this.props.history so that we use the redux store instead of the state
  • WeatherInfoPanel.jsx — added a method to toggle between Celsius and Fahrenheit depending on which metric was chose and replacing each static field with their dynamic counterpart from the redux store of the weather data.

Checkpoint #10https://github.com/JinKim7/mern-weather-app/tree/10.redux
Files Changeshttps://github.com/JinKim7/mern-weather-app/pull/11/files

Run the app locally (npm run dev) and this should be near-final product:

Gif on looking through the redux states after saving form.
screenshot of the states that change when saving the form

--

--