Serving a React app from a Raspberry Pi

Serving a React app from a Raspberry Pi

ยท

6 min read

In this blog, I'll share my journey of transforming my Raspberry Pi into a React app server. We'll start by installing nginx followed by configuring nginx for optimal React app hosting.

Prerequisites

  • nginx installed in your Raspberry pi. The installation guide can be found here.

  • LTS version of Nodejs installed in your Raspberry pi.

  • git

Connecting to the Raspberry Pi

๐Ÿ’ก
This section should be followed if you want to use the Pi in headless mode .i.e without keyboard or mouse.

A SSH connection can be established between your local machine and a Raspberry Pi provided

  1. They are on the same network

  2. Raspberry Pi is configured to accept SSH connections

Follow this guide to enable SSH and connect the Raspberry Pi to a network.

After the Pi has connected to the wifi, you can use tools like nmap or arp-scan to figure out the IP address of the Raspberry Pi.

Once we have the IP address of the Raspberry Pi, we can connect to it via SSH.

๐Ÿ’ก
SSH syntax is <username>@<IP address>

Configuring nginx

Understanding nginx.conf

The way nginx and its modules work is determined in the configuration file. By default, the configuration file is named nginx.conf and placed in the directory /usr/local/nginx/conf, /etc/nginx, or /usr/local/etc/nginx.

user <USER>;
error_log /var/log/nginx/error.log notice;
pid /var/log/nginx/pid.log;

events {
    worker_connections 1024;
}

http {
    listen 80;
    server {
        location / {
            root <LOCATION>;
        }
    }
}

The above block represents a sample configuration,

  • user - Defines user credentials used by worker processes

  • error_log - Configures logging. The first parameter defines a file that will store the log. The second parameter determines the level of logging, and can be one of the following: debug, info, notice, warn, error, crit, alert, or emerg.

  • pid - Defines a file that will store the process ID of the main process.

  • events - Provides context in which the directives that affect connection processing are specified

    • worker_connections - Sets the maximum number of simultaneous connections that can be opened by a worker process
  • http - Provides context in which the HTTP server directives are specified

    • listen - Sets the address and port for IP, or the path for a UNIX-domain socket on which the server will accept requests. Both address and port, or only address or only port can be specified.

    • server - Sets configuration for a virtual server

      • location - Sets configuration depending on a request URI

        • root - Sets the root directory for requests

nginx.conf for a simple HTTP server

user <USER>;
error_log /var/log/nginx/error.log notice;
pid /var/log/nginx/pid.log;

events {
    worker_connections 1024;
}

http {
    listen 80;
    server {
        location / {
            root /data/www;
        }
    }
}
๐Ÿ’ก
<USER> should be replaced by the non-root user in your Raspberry Pi. You can also create a user called nginx by following this guide.

Verify the configuration

Create a sample index.html file in /data/www/. This will be our sample HTML file which will be served by nginx

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Title</title>
  </head>
  <body>
    <p>Hello World</p>
  </body>
</html>

Following this, start nginx by using

sudo systemctl start nginx
๐Ÿ’ก
If you are able to access Raspberry Pi through its GUI, then you can navigate to 127.0.0.1/ from the browser in it to see the HTML file

We can view the HTML file from the local machine by navigating to the IP address of the Raspberry Pi from the browser.

Setting up the React App

We will be using a basic weather application. The react app uses an external API to fetch weather data of a place. It uses this API to fetch weather data. The API uses a token to authorise its users, hence users have to register to the free tier.

Instead of using the API directly from the client, we will have nginx act as a proxy between the client and the API. In order to achieve this, the app will make a GET request to /weather/place/{location}.

fetch(`/weather/place/${location}`).then(res => res.json()).then(data => {
      // Do something with the data
})

If the user wants to know the weather in Delhi, the resulting URL of the GET request will be http://<RASPBERRY PI IP ADDRESS>/weather/place/Delhi.

๐Ÿ’ก
The full code of the react app is here.

Setting up nginx.conf for the Weather App

Weather API can be accessed using the following endpoint:

http://api.weatherapi.com/v1/current.json?key=<YOUR KEY>&q=<LOCATION>&aqi=no

This will return a JSON response containing the weather data like temperature, humidity, wind speed, etc. We will use this in nginx.conf as follows:

user srinivas;
error_log /var/log/nginx/error.log notice;
pid /var/log/nginx/pid.log;

events {
    worker_connections 1024;
}

http {
    listen 80;
    server {
        location ~* ^/weather/place/(.*)$ {
            resolver 8.8.8.8;
            proxy_pass http://api.weatherapi.com/v1/current.json?key=<YOUR KEY>&q=$1&aqi=no;
        }
        location / {
            root /data/www;
        }
    }
}
๐Ÿ’ก
<YOUR KEY> should be replaced by the key from weatherapi.com
  • proxy_pass - Sets the protocol and address of a server and an optional URI to which a location should be mapped. As a protocol, โ€œhttpโ€ or โ€œhttpsโ€ can be specified. The address can be specified as a domain name or IP address, and an optional port.

  • resolver - Configures name servers used to resolve names of upstream servers into addresses. 8.8.8.8 is Google's DNS.

location ~* ^/weather/place/(.*)$ {
    resolver 8.8.8.8;
    proxy_pass http://api.weatherapi.com/v1/current.json?key=<YOUR KEY>&q=$1&aqi=no;
}

The above block contains ~* which is a modifier that allows for case insensitive matching with regular expressions. ^/weather/place/(.*)$ is a regular expression that matches /weather/place/Delhi or any other location.

(.*) creates a capture group (you can read more about capturing groups here). Essentially, it groups a sub-pattern allowing us to use it as a single entity. These groups are numbered starting from 1 and can be accessed via the $ notation.

Therefore, in the following line, $1 references the location that the user has provided.

proxy_pass http://api.weatherapi.com/v1/current.json?key=<YOUR KEY>&q=$1&aqi=no;
๐Ÿ’ก
Regex 101 is a great website that breaks down regular expressions and highlights each group

Serving Weather App from the Raspberry Pi

In order to serve the react app from the Raspberry Pi, we need to

  1. Get the application in the Pi

  2. Build the application

  3. Move the build files into /data/www

  4. Load nginx.conf with the new configuration

  5. Reload nginx

Getting the application into the Pi

Create a public GitHub repository for the react application. Push the code into it and clone it from the Raspberry Pi.

Building the application

Move into the code directory and run the following command:

npm run build

Moving the build files

Move the files into /data/www. This can be done using the mv command.

mv <SOURCE> /data/www/

Here's how my /data/www directory looks:

Loading the nginx.conf

Modify the nginx.conf file as follows:

user srinivas;
error_log /var/log/nginx/error.log notice;
pid /var/log/nginx/pid.log;

events {
    worker_connections 1024;
}

http {
    listen 80;
    server {
        location ~* ^/weather/place/(.*)$ {
            resolver 8.8.8.8;
            proxy_pass http://api.weatherapi.com/v1/current.json?key=<YOUR KEY>&q=$1&aqi=no;
        }
        location / {
            root /data/www;
        }
    }
}

Reload nginx

Run the following command:

nginx -s reload

Viewing the Weather app

๐Ÿ’ก
If you are able to access the GUI, navigate to 127.0.0.1/ from the browser to view the website.

From your local machine, navigate to the IP address of your Raspberry Pi and you should be able to view the weather app.


The full code along with the nginx.conf file is available here.

ย