Create a simple weather app using Node.js, Express, and React

Maison Moa
17 min readMay 27, 2018

--

Tired of needing to actually go outside to see what the weather is like? Annoyed of using <insert popular weather app here> and just can’t go on without making your own? Probably not if I had to guess.

But there’s one certainty; you can learn a thing or two by making things. This simple weather app will will only display the current weather for an area. But, there is a lot to learn along the way of making it, especially if you haven’t done something like this yet! We have to create a frontend + backend, add routes, get data from a 3rd party API, use this API data with a React frontend, and we have to route React components around.

There are way simpler ways to create this application; for instance we could accomplish it with just React alone. However, the two main points(and reasons) of this are to show how to set up routes in a backend server that can fetch 3rd party API data, and how to use this backend with a React frontend.

Below, we start by creating a backend Express server that fetches weather data from a weather API, and we end with creating a React frontend that receives this weather data from the backend. The React side of this was bootstrapped with the lovely create-react-app. And an API provided by www.openweathermap.org/api will send current weather data (by location) into the backend.

To get started, I like to talk through and breakdown a project into smaller, more digestible chunks. More times than not, deconstructing something into smaller pieces can make it seem way less daunting, and definitely can give a more clear direction of where to start.

The basic gist of this weather application:

  • We will need to set up a backend (Express) server that connects to a front end client (React).
  • Can’t have a weather app with no weather data! So we will make use of a 3rd party weather API to provide data. We need to connect to that API and use the returned weather data.
  • We have to collect the users zipcode and combine that with the weather API’s URL to request location specific data.
  • We need to validate the zipcode, and also account for any potential 404 errors when receiving data back from the weather API.
  • Finally we have to display the current weather using the weather API data. Using React, we will need to create a component that is responsible for a page that does this.

No simpler than that, this project has been broken down into a few basic bullet points that are way easier to digest than tackling it all head on.

Please note: I am going to be assuming you are somewhat familiar with React and Node.js, have set up a basic Express server before, at least have played around with a 3rd party API, and are also comfortable working from the terminal (or command prompt).

Yeah, yeah, yeah. Let’s get setup!

First, if you plan to follow along I would advise heading over to Github and cloning this project. Grab it here!

As noted above, we will be using a 3rd party weather API from Open Weather Map to get weather data. I recommend checking out the docs for the API we will be using here at: www.openweathermap.org/current. Take note of what the returned weather data looks like, and check out the ‘By Zip code’ section.

** YOU WILL NEED AN API KEY TO USE THE CLONED PROJECT ***

You can sign up for free (and with no payment details) here at: https://openweathermap.org/api to get access to an API key. I have zero affiliation with Open Weather Map, it just happens to be the API I use for weather stuff.

Once signed up, API keys will be available in the ‘API Keys’ section found inside your account’s homepage. You can then copy and paste the free API key into the application here in /server/routes/api/SearchLocations.js:

Add you API Key to line 5

Make sure to redact the ‘< >’ from the URL string after you’ve pasted your API key. Normally your API key would be hidden away in an environment variable, but for this example, we’re just going to make it as straight forward as possible. Please note that you never want to expose your API keys in production.

Installing dependencies

  • Once the repository is cloned locally, go ahead and access the /weather_app_tutorial directory from the terminal and run npm install to install the backend dependencies from the package.json file.
  • While still inside /weather_app_tutorial navigate into the /client directory and then do the same thing by running npm install to install all of the dependencies that are required for React.

Once the dependencies are installed, the boilerplate is ready!

The backend Express server

To get things started, we are going to set up the Express server that will be responsible for all of the backend routes, partly responsible for routing the pages, and will also be responsible for making calls to the 3rd party weather API.

Without the backend server, the React client would still run and do it’s thing regardless, it would just have zero connectivity to the backend (duh!). This is because React is running on it’s own server (port 3000), and actually connects to the backend (port 5000) by way of a proxy. This is important to know, because this is what is going to tie our frontend to it’s backend. If you check the package.json in the /client directory, you will see where this proxy has been added.

After installing all of the dependencies above, Express will now be available to use. In the root directory locate/server/server.js and open it up.

server.js

This is a quick and easy Express server that runs on port 5000.

Let’s start it to make sure everything is working. From the terminal navigate into /server and run node server.js to start up the Express server. If you head over to http://localhost:5000/ in your web browser, you should see ‘PORT 5000’. This confirms the server is up and running.

Setting up the API routes

Yeah, we could technically set up all of our routes in server.js like a bunch of cavemen, but fortunately we aren’t cavemen. A more organized approach is to set up a /routes directory that server.js can import routes from!

To overview: The /routes directory will contain any routes we need to create for this application, and then we will set up server.js to import those routes so that our front end client (React) can use them.

First we will set up the API route that is responsible for fetching weather data from the 3rd party weather API:

Open up the SearchLocations.js file that is located in the /routes/api directory. This file will eventually handle calling the weather API and capturing the users zip code from the home page (more on that later).

Take a look at SearchLocations.js:

SearchLocations.js

At first glance, we are creating a module that can be exported, hence module.exports on line 3. This module will eventually be imported elsewhere making the routes available for use.

Let’s breakdown what is going on with these POST and GET routes:

The POST route

POST route

In SearchLocations.js we have a POST route that a front end input form can use to send the users zip code to the backend when submitted. We are setting let zipcode to the value of the front end input form once it’s submitted. Note that we keep the let zipcode variable outside the scope of the POST route for a reason. It will be needed when we make our request to the weather API, so we need it to be global in this case.

We can quickly validate the zip code, and if needed, route the user to an error page. The if statement seen in the POST route handles this for us. In the US a zipcode is only 5 numbers in length. If a user enters a US zip code shorter than 5 numbers, greater than 5 numbers, or nothing at all, our POST route will redirect to the error page. Per the docs, when looking up data by zip code, the Open Weather API defaults to the US country code. This is the country code the zip code is validated for here.

We then use res.redirect('/current-weather') to redirect the page to the /current-weather URL once a user submits a valid zip code. Once the user submits a valid zip code in the front end, the input form will call the POST route in the backend, and this POST route then triggers the redirect of the page. When the page is redirected to /current-weather , this URL (with the help of React Router) will render a React component that will utilize the backend GET route.

The GET route

GET route

This GET route is called when the React component CurrentWeather.js mounts to the DOM. Remember how I said that our POST request would redirect to a URL that renders a React component via React Router? CurrentWeather.js will be that component, but for now that is not important. When we get to working through the React side of this project, I will go into more detail regarding how and when this GET route is used.

Before moving on: Check out the API Docs for the API endpoint we will be calling below. It goes over in detail how to get specific location data, and it also goes over what data is returned.

In the GET route you’ll notice const baseUrl & const apiId right away. I have split up the API endpoint URL so that it is possible to insert the users zip code into the URL string. Since the let zipcode variable is available globally and was previously assigned a value by the POST route, we can use it in our GET route to build the weather API URL specific to the users location.

There are probably more elegant ways to split a URL and insert a string, but for a quick tutorial, this works just fine. For now const userLocation(url1, url2, zipcode) is a function that returns a string consisting of both halves of the API URL plus the users zipcode. So if you can imagine, it returns an entire URL with the user’s zipcode inserted.

We take this newly created URL now available in const apiUrl and with using this new URL in a fetch request, our backend server goes out into deep space and brings us back some weather data. If you have ever used fetch before, this is pretty standard stuff. We fetch the data, convert the response into json , and then finally we send it somewhere. When we eventually get to making a fetch request from our React front end, res.send({ data }) will be the line of code that sends the weather data from the backend to the front end fetch request.

Export the routes!

We now need to make our API routes available to the backend server. At this point our /routes directory is just hanging out by its lonesome, independent from our server.js file. As you can guess, we need to change that. Here’s how it’s accomplished.

If you take a quick look, you’ll notice in our /routes directory that we have an index.js file in addition to another directory named /api . This is an organizational tactic that separates the API routes into the /api directory, while the single index.js file handles gathering all of the routes and exporting them.

Basically we keep all of the routes like SearchLocations.js inside of the /api directory, and use the index.js file to handle exporting them. We could have 20 additional routes added to /api but regardless of how many, our single index.js file will loop through all of the available routes and export them. Let’s look at how that is set that up.

Open up server/routes/index.js:

The fs.readdirSync block of code finds and loops through all of the files in the /routes/api folder, and for each file it finds, it requires it. This is important as it allows us to export all of the routes found in the /routes/api directory. You can see how simple and organized it is to use the single index.js file to bring all of the routes together and export them at one point. Sure we only have one route for now, but this makes all the difference when things start getting more complex.

Import the routes!

This is going to happen in server.js so open it up:

server.js

require('./routes)(app) is going to import the /routes folder that contains our index.js file. Remember, the index.js file is what’s responsible for exporting all of the routes in /routes/api. By importing the /routes directory that the index.js file is located in, our backend sever will have access to every API route we create. Sweet!

Kick back and relax, that’s the backend. We can now move onto the final half of this tutorial, and that’s setting up the front end!

The front end

For this weather application I wanted to use React. If you have cloned the Github repo, you’ve probably noticed a /client directory. This directory is our React application.

Back when we first set up the project, you should have navigated to the /client directory from the terminal and ran npm install to install all of the dependencies for React. There should be a node_modules folder located inside of /client if so.

From here on our unless otherwise noted, we will be in the /client directory.

index.js

Lets get started with /src/index.js. This file is responsible for rendering our React components to the ‘root div’ (otherwise known as the entry point) in public/index.html. Aside from that, this is where we will also be using React Router to route our React components.

To start things off, go ahead and check out /src/index.js:

index.js

To briefly explain, on line 8–11 we are importing React components so that we can render them.

Line 15–21 is where we use React Router to ‘assign’ a react component to a URL path. Lets take a deeper look at line 17, 18 & 19. The '/' path on line 17 indicates that we are home. If you notice when we route to the '/' path we also render the Home.js component. Anytime the browser URL is www.ourapplication.com/ our home page component renders.

Line 18 and 19 is no different. When we visit the /current-weather path in the browser, the CurrentWeather.js component is rendered. And if we visit the /error path, ErrorDisplay.js is rendered. Easy!

Let’s start the React server!

From the terminal, navigate into the /client directory and then run npm start. This will start up the React server on port 3000. (Check out: http://localhost:3000/ if the React scripts did not launch the application automatically.)

After firing up the React server, you should see the following:

This is the home page! Albeit it simple, it does the job. You can see the input field that will capture the user’s zip code.

The Home Page

Let’s go ahead take a peak at the code for this Home.js component:

Home.js

What I want to detail is line 18–21. This is our input form that we will use to capture the user’s zip code. The form method is set to POST, and the action is set to /search-location. Remember above when we createdthe route SearchLocations.js? Well the app.post('/search-location') route inside of that file is what our input form is posting it’s value to. When we post the input form value to app.post('/search-location') it then takes this value (which will be the zip code), validates it and then assigns it to a global variable that we can use elsewhere.

In short:

  • The input form posts it’s value to a POST route in the backend.
  • This backend POST route then assigns the received input value to a variable so we can use it later when calling the weather API.

No sweat, we are are very close to being done! Just one more important piece to this puzzle.

The Current Weather Component

This component is where a lot of the front end magic happens. It is responsible for rendering a current weather tile with all of the data we will be receiving from the 3rd party weather API in the backend.

First things first, open up and let’s take a look at /src/views/CurrentWeather.js:

CurrentWeather.js

Here’s what it looks like with loaded weather data:

Not a lot going on, but you can build from here!

For now lets focus in on the componentDidMount(). If you’re familiar with React, you will know that any code inside of componentDidMount() will be executed when the component mounts to the DOM. This is also a prime place to make an API call. Cue the fetch request that is made inside of componentDidMount().

Notice the path /search-location-weather that we are fetching. If you take a moment check out /server/routes/api/SearchLocations.js, you will notice that /search-locations-weather is a GET route. Inside of this GET route, we actually fetch the weather data from the 3rd party API. Here is also where we finally make use of that global let zipcode variable that was set in the initial POST route.

So when our React component mounts to the DOM, componentDidMount() will automatically execute the contained fetch request to the backend GET route, and in turn, it will receive back the weather API data that our backend GET route fetched. After the weather data is received in the front end, we first check to make sure there was not a 404, and if successful, we then set the component’s state to the weather data that is returned. If the weather API fetch returned a 404, we return an error message in our weather tile.

As mentioned at the beginning, I am assuming here that you are somewhat familiar with React. I won’t be going into to much detail regarding how the fundamentals work.

To sum it up:

  • When a valid zip code is entered in Home.js and submitted, the page is redirected to /current-weather which because of React Router, will render CurrentWeather.js.
  • When the CurrentWeather.js component mounts to the DOM, it automatically makes a fetch request to the backend GET route.
  • This backend GET route then goes out and fetches data from a weather API using the users zip code captured in the initial POST route to fetch location specific weather data.
  • The backend GET route then sends the fetched weather data back to the front end client in response to the fetch request made in componentDidMount().

Looking at the weather data

What does this fetched weather data look like? Say no more! If you have checked the API docs here, you may already be familiar with what the returned data looks like. But nonetheless, here it is:

This is what the backend GET route's fetch request will receive from the weather API and then send to the front end.

Using the weather data

If you check out the this.state = ({ }) block of code in CurrentWeather.js you will see what weather conditions we are going to be using. Obviously you can play around with this and add more conditions from the weather API on your own, but these are the conditions we are working with for this tutorial.

The fetched weather data is accessed just like any Javascript object would be. The fetch request returns data, turns it into JSON, and then allows us to access the data as normally expected with JSON. We then update the state with this data. Notice that this occurs inside of the fetch request’s then() promise. This way, only when data is returned the state is updated. If no data has been returned, a loading icon will appear until data shows up.

Are we loading?

The isLoading: true in this.state = ({ }) is set to a boolean that will let our component know if the weather data is loading or not. When the fetch request made in componentDidMount() returns data and updates the state of the component, it also updates the loading value to isLoading: false to let our component know to render the current weather tile instead of the loading icon.

Weather Icons

Let me explain how we are checking what weather icon we need to display. First, please check this chart out. This link explains and shows the list of various weather condition codes the weather API sends with the response data. After checking out what weather condition codes correlate to what icons, you’re good to go.

Basically blocks of numerical weather codes refer to different weather conditions. You can have an icon for any condition code, but I chose to stick with the basic icons: Thunderstorm, Rain, Snow, Clear, or Clouds. For brevity, I give all similar conditions the same icon. For instance, here we give all cloudy conditions the same icon, be it partly cloudy or covered. Same goes for rain conditions and snow.

First, the weather API data returns a weather ID. Then a series of if statements take the returned weather ID and check if it’s value falls between certain spans of integers. It’s pretty easy to see exactly what the if statements are checking for. If our weather API returned weather code satisfies any of these if statements, the state is updated with the corresponding weather icon.

If you take a look at the top of CurrentWeather.js you will see the SVG weather icons we are importing for use. Finally, the weatherIcon: '' value in this.state = ({ }) is what gets updated to the weather icon after we determine which weather icon should be used.

For example: Say we get back a weather ID of 800. Per the docs, the 800 ID correlates to a certain weather condition code. In this case, 800 refers to clear conditions. So we set update the state: this.state = ({ weatherIcon: clearIcon }) — so our component can use the clear weather icon.

Dealing with a 404

If we fetch the weather API with a valid US zip code and for whatever reason it can not find the location, it will return a 404 like so:

We can use this ‘404’ message in CurrentWeather.js to trigger an error message. This is that error message view:

The fetch request is going to either return valid weather data or it will return a 404. This is the block of code that is responsible for checking for a 404 condition:

Inside of const WeatherConditions we use a ternary operator to first check to see if this.state.cityNotFound is equal to 404. Remember that if our weather API fetch returns a 404 message, we set cityNotFound to 404. If true, const WeatherConditions will return the error view. Otherwise, if there was no error, the current weather view is returned.

Lastly, notice the error view has a return ‘Try Again’ button. This will allow the user to go back and re-enter their zip code. This navigation is handled by React Router. On line 15, you can see where the <button> tag is wrapped by <Link>. This will re-route the browser URL to the home page once the button is clicked.

And that’s that.

Just like that, we’ve come to a screeching halt. But hey, it’s gotta end somewhere! If you’ve made it this far, I do hope I have helped you learn something and I thank you for taking the time to read this!

Again you can find the repo for this tutorial here: Weather App Tutorial

Please report any questions or errors in the article!

--

--

Maison Moa

Curious Web Developer, avid Golfer, and a decorated veteran of the great war against procrastination.