Building a Visitor Tracker With Robyn and React| Python
Tags: react, robyn, python, typescript
Published: 2024-01-14 23:59:08

---
title: Building a Visitor Tracker With Robyn  and React| Python
published: true
date: 2023-12-18 01:23:08 UTC
tags: #react #robyn #python #typescript
canonical_url: https://carlosmv.hashnode.dev/building-a-visitor-tracker-with-robyn-and-react-python
---

In this article, we are going to build a Visitor Tracker with Robyn and React.

This is not a new project, I have built the same project using FastAPI and Rails. But, I want to build this app with Robyn to show features that I have not used in previous demos, like middleware, the request object, and the CORS. Also, to show the use of the response object.

### Requirements

- Python installed

- Basic Python knowledge

- Pip installed

- Postgres installed

- NodeJs installed

- Basic React Knowledge

### Building the Visitor Tracker

First, we create a directory for this application.

```
mkdir visitor_trackercd#Windows userspy -m venv venvcd venv/Scripts./activate#Linuxpython3 -m venv venvsource venv/bin/activate
```

We install all the dependencies we need.

```
pip install robyn psycopg2-binary python-dotenv
```

Inside the `visitor_tracker` directory, we create a new file, [`init_db.py`](http://main.py)`.`

**init\_** [**db.py**](http://main.py)

```
import osimport psycopg2from dotenv import load_dotenvload_dotenv()PASSWORD = os.getenv('PASSWORD')def get_db_connection(): conn = psycopg2.connect( dbname = "logs_db", user = "postgres", password = PASSWORD ) return connconn = get_db_connection()cur = conn.cursor()cur.execute('DROP TABLE IF EXISTS logs;')cur.execute('CREATE TABLE logs (id serial PRIMARY KEY,' 'ip_address varchar (150) NOT NULL,' 'request_url varchar (50) NOT NULL,' 'request_path varchar (50) NOT NULL,' 'request_method varchar (50) NOT NULL,' 'request_time timestamp (50) NOT NULL,' 'date_added date DEFAULT CURRENT_TIMESTAMP);' )cur.execute('INSERT INTO logs (ip_address,' 'request_url,' 'request_path,' 'request_method,' 'request_time)' 'VALUES (%s, %s,%s, %s, %s)', ('127.0.0.1', 'http://localhost:8000', '/', "GET", "2023-06-25T16:03:24.722256", ))conn.commit()cur.close()conn.close()
```

Here we set up a database connection to store log data. First, we load the `.env` file using `dotenv` to get the database username and password variables. Then, we define a `get_db_connection()` function that establishes a connection to a PostgreSQL database named logs\_db. Then, the code calls that function to get a database connection and cursor.

In this file, also the code drops the logs table if it exists and recreates it with the given schema - with columns to store IP address, request URL, port, path, method, time etc. It inserts sample log data into the table with its values. It commits the changes to the database and closes the cursor and connection.

[**helpers.py**](http://helpers.py)

```
import collectionsdef to_dict(psycopg_tuple:tuple): tracker = collections.OrderedDict() tracker['id'] = psycopg_tuple[0] tracker["ip_address"] = psycopg_tuple[1] tracker["request_url"] = psycopg_tuple[2] tracker["request_path"] = psycopg_tuple[3] tracker["request_method"] = psycopg_tuple[4] tracker["request_time"] = psycopg_tuple[5].strftime("%d-%m-%Y, %H:%M:%S") return trackerdef list_dict(rows:list): row_list = [] for row in rows: book_dict = to_dict(row) row_list.append(book_dict) return row_list
```

This file has two functions: `to_dict()` and `list_dict()`. The `to_dict()` function converts a PostgreSQL tuple to a dictionary. The `list_dict()` function converts a list of PostgreSQL tuples to a list of dictionaries.

The `to_dict()` function takes a PostgreSQL tuple as input and returns a dictionary. The dictionary contains the values of the tuple in the same order as the tuple. The `list_dict()` function takes a list of PostgreSQL tuples as input and returns a list of dictionaries. The dictionaries are created using the `to_dict()` function.

**controllers.py**

```
from init_db import get_db_connectionfrom helpers import to_dict,list_dictimport jsondef all_logs(): conn = get_db_connection() cur = conn.cursor() cur.execute('SELECT * FROM logs;') logs = list_dict(cur.fetchall()) cur.close() conn.close() return logsdef new_log(ip_address: str, request_url: str, request_path: str, request_method: str, request_time: str,): conn = get_db_connection() cur = conn.cursor() cur.execute('INSERT INTO logs (ip_address, request_url, request_path, request_method, request_time)' 'VALUES (%s, %s, %s,%s, %s) RETURNING *;',(ip_address, request_url, request_path, request_method, request_time, )) log = cur.fetchone()[:] log_dict = to_dict(log) conn.commit() cur.close() conn.close() return json.dumps(log_dict)
```

The `all_logs()` function gets all logs from the database and returns a list of dictionaries. Each dictionary contains information about a single log.

The `new_log()` function inserts a new log into the database.

**middleware.py**

```
from robyn.robyn import Requestfrom datetime import datetimeclass Tracker: def visitor_tracker(request: Request): ip_address = request.ip_addr request_url = request.url.host request_path = request.url.path request_method = request.method request_time = str(datetime.now()) return { "ip_address": ip_address, "request_url": request_url, "request_path": request_path, "request_method": request_method, "request_time": request_time, }
```

Here we create the Tracker class and define the `visitor_tracker` function.

The `visitor_tracker` function returns the attributes: `ip_address`, `request_url`, `request_path`, `request_method`, `request_time`.

**app.py**

```
from robyn import Robyn, status_codesfrom robyn.robyn import Response, Headers, Requestfrom controllers import all_logs, new_logfrom robyn import loggerfrom middleware import Trackerimport jsonapp = Robyn( __file__ )@app.before_request()async def log_request(request: Request): tracker = Tracker.visitor_tracker(request=request) new_log(tracker["ip_address"], tracker["request_url"], tracker["request_path"], tracker["request_method"], tracker["request_time"]) logger.info(f"Received request: %s", tracker) return request@app.get("/")async def hello(): return Response(status_code=status_codes.HTTP_200_OK,headers=Headers({}), description="Hello, World!")@app.get("/visitors")async def hello(): logs = all_logs() return Response(status_code = status_codes.HTTP_200_OK, headers=Headers({}), description = json.dumps(logs))app.start(port=8000, host="0.0.0.0")
```

Next, in the `app.py` file we define a middleware that intercepts all the requests using the `before_request()` decorator. This middleware will store the request's IP address and other attributes, and it will create an instance of the Tracker class. Then it will store the data in the database.

Also, here we define two routes: `"/"` and `"/visitors"`. In the hello() function we retrieve all the logs in the database or the visitors' information and show them using the Response class. The Response class requires `status_code`, `headers` and `description` as positional arguments.

## Building a UI

We will create a React app to visualize the tracking information in a table.

### Adding CORS

**app.py**

```
from robyn import Robyn, status_codes, ALLOW_CORSfrom robyn.robyn import Response, Headers, Requestfrom controllers import all_logs, new_logfrom robyn import loggerfrom middleware import Trackerimport jsonapp = Robyn( __file__ )ALLOW_CORS(app,["*"])...
```

In Robyn, we import ALLOW\_CORS and it needs the instance of Robyn, in this case, the `app` variable and the origins. I use `["*"]` to allow requests from any origin.

### Installing Vite and React

In our command line, we install Vite with a React-Typescript template. This command line will create a folder for the project.

```
#npmnpm create vite@latest table -- --template react-ts#yarnyarn create vite@latest table --template react-ts#pnpmpnpm create vite@latest table --template react-ts
```

After all the packages are installed we run Vite with the command:

```
npm run dev
```

We navigate to [`localhost:5173`](http://localhost:5173) and we should see the Vite and React homepage.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1690158293818/39a8a978-93c9-47d5-ab37-49f8964ad67d.png?auto=compress,format&format=webp)

**app.ts**

```
import React, { useState, useEffect} from "react";const url = "http://localhost:8000/visitors";interface Table { id: number, ip_address: string, request_url: string, request_path: string, request_method: string, request_time: string,}const Table: React.FC = () => { const [data, setData] = useState<Table[]>([]); useEffect(() => { fetch(url) .then(res => res.json()) .then(data => setData(data)); console.log(data); }, []); return ( <div> <h1>Logs</h1> <table> <thead> <tr> <th>Id</th> <th>IP Address</th> <th>Request URL</th> <th>Request Path</th> <th>Request Method</th> <th>Request Time</th> </tr> </thead> <tbody> {data.map((item, index) => ( <tr key={index}> <td>{item.id}</td> <td>{item.ip_address}</td> <td>{item.request_url}</td> <td>{item.request_path}</td> <td>{item.request_method}</td> <td>{item.request_time}</td> </tr> ))} </tbody> </table> </div> );};export default Table;
```

We start the Robyn server and the Vite server simultaneously. And then, we navigate to `localhost:5173`, we should see the following page:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702339060210/00ca3693-4389-4ee7-87e0-4efbf13a1d2d.png)

Let's add a `.css` file to add style and see the data easily.

**index.css**

```
:root { font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; line-height: 1.5; font-weight: 400; color-scheme: light dark; color: rgba(255, 255, 255, 0.87); background-color: #242424; font-synthesis: none; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; -webkit-text-size-adjust: 100%;}table { border-collapse: collapse; margin-bottom: 1rem;}th,td { padding: 0.5rem; border: 1px solid #ccc;}th { text-align: left;}td { text-align: left;}.column-gap-10 { column-gap: 10px;}
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702339348485/37115e63-f268-480a-8fdb-387df12802de.png)

### Conclusion

In this article, we learned how to build a visitor tracker with Robyn and React. We use Robyn's middleware to intercept and store the information of the requests. Also, we learned how to allow CORS in Robyn to make it possible to integrate with any frontend framework.

The source code is [here](https://github.com/carlosm27/robyn_visitor_tracker).

Thank you for taking the time to read this article.

If you have any recommendations about other packages, architectures, how to improve my code, my English, or anything; please leave a comment or contact me through [**Twitter**](https://twitter.com/Carlos_marcv), or [**LinkedIn**](https://www.linkedin.com/in/carlos-marcano-a2135a134/).

### Resources

[Robyn documentation](https://robyn.tech/)