Sunat Exchange

Sunat Exchange

Backend/DevOps project that tracks Peru’s SUNAT exchange rate, stores historical data in PostgreSQL, syncs daily with cron, exposes JSON endpoints with Flask, and serves a Chart.js dashboard behind Nginx using Docker Compose.

Documentation

SUNAT Exchange Tracker

A Dockerized Flask application that tracks Peru’s SUNAT exchange rate, stores historical data in PostgreSQL and displays it through a Chart.js dashboard behind Nginx.

This project was built to demonstrate backend and DevOps skills around API design, external data ingestion, database persistence, scheduled jobs, Docker Compose infrastructure, testing and deployment-oriented documentation.


Project Summary

sunat-exchange is a backend/DevOps portfolio project focused on collecting and exposing exchange rate data from SUNAT.

The application fetches exchange rate information, normalizes it, stores historical records in PostgreSQL and provides both a JSON API and a simple web interface with a historical chart.

The stack runs with Docker Compose and separates responsibilities across multiple services:

  • Flask application for API and web rendering.
  • PostgreSQL for historical persistence.
  • Cron service for scheduled synchronization.
  • Nginx as the public entry point.
  • Chart.js for historical visualization.
  • pytest and GitHub Actions for validation.

This makes the project more complete than a simple API script because it includes persistence, scheduled jobs, reverse proxy configuration, tests and containerized execution.


What This Project Demonstrates

This project shows my ability to build and operate a small backend service with real infrastructure concerns.

Key skills demonstrated:

  • REST API development with Flask.
  • External data consumption and normalization.
  • PostgreSQL persistence.
  • Historical data storage.
  • Scheduled synchronization with cron.
  • Docker Compose multi-service architecture.
  • Nginx reverse proxy configuration.
  • Gunicorn application serving.
  • Chart.js data visualization.
  • Environment-based configuration.
  • Backup and restore workflows.
  • Automated testing with pytest.
  • CI validation with GitHub Actions.
  • Backend documentation for maintainability.

Problem

Exchange rate data is useful for many financial, accounting and business workflows, but raw public data often needs to be collected, normalized and exposed in a cleaner way.

A useful backend service for this problem should answer questions such as:

  • How can exchange rate data be fetched from an external source?
  • How can the data be normalized into a consistent format?
  • How can historical values be stored?
  • How can the latest value be exposed through an API?
  • How can the data be visualized?
  • How can the synchronization process run automatically?
  • How can the service be executed consistently in another environment?
  • How can the project be tested and validated?

sunat-exchange was created to solve those concerns in a small but realistic backend project.


Solution

The solution is a Docker Compose application that combines a Flask backend, a PostgreSQL database, a cron-based synchronization service and an Nginx reverse proxy.

The Flask application exposes JSON endpoints and renders a simple dashboard. The database stores historical exchange rate records. The cron service keeps the data updated automatically. Nginx is the only public entry point, while internal services remain isolated inside Docker networks.

This structure makes the project useful as a portfolio piece because it demonstrates both backend development and deployment-oriented thinking.


Architecture

                    User / Browser / API Client
                              β”‚
                              β–Ό
                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                        β”‚   Nginx   β”‚
                        β”‚  Reverse  β”‚
                        β”‚   Proxy   β”‚
                        β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β–Ό
                       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                       β”‚ Flask App   β”‚
                       β”‚ API + Web   β”‚
                       β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
                             β”‚
              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
              β–Ό                             β–Ό
      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”             β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
      β”‚  PostgreSQL   β”‚             β”‚   Chart.js   β”‚
      β”‚ Historical DB β”‚             β”‚  Dashboard   β”‚
      β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜             β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              β”‚
              β–Ό
      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
      β”‚ Cron Service  β”‚
      β”‚ Daily Sync    β”‚
      β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
              β”‚
              β–Ό
       External Exchange
        Rate Data Source

Stack

LayerTechnology
BackendFlask
WSGI ServerGunicorn
DatabasePostgreSQL
Reverse ProxyNginx
Schedulercron
VisualizationChart.js
ContainersDocker
OrchestrationDocker Compose
Testingpytest
CIGitHub Actions
AutomationMakefile

Main Features

Flask Backend

The backend is built with Flask and exposes both JSON endpoints and a simple web page.

The API allows clients to retrieve exchange rate data, while the web interface displays historical values in a visual format.


PostgreSQL Historical Storage

The project stores exchange rate records in PostgreSQL instead of keeping only temporary or in-memory data.

This allows the application to maintain a history of values over time and makes the service more realistic from a backend perspective.

The database layer handles:

  • Exchange rate persistence.
  • Historical queries.
  • Date-based storage.
  • Conflict handling for repeated syncs.
  • JSON serialization for API responses.

Daily Synchronization with Cron

A dedicated cron service is responsible for running scheduled synchronization tasks.

This separates the data collection process from the web application and makes the architecture closer to a real production-style service.


Nginx Reverse Proxy

Nginx is used as the public entry point for the application.

Only Nginx exposes a port to the host. The Flask app, PostgreSQL and cron service remain internal to the Docker Compose network.

This demonstrates a safer and cleaner service exposure pattern.


Chart.js Dashboard

The project includes a simple frontend dashboard using Chart.js to visualize historical exchange rate data.

This gives the project a user-facing layer without turning it into a large frontend application.


Docker Compose Setup

The project is designed to run with Docker Compose.

The stack includes separate services for:

  • nginx
  • web
  • cron
  • db

This makes the project reproducible and easier to run in local or server environments.


Testing

The project includes tests with pytest to validate important parts of the application.

Testing helps verify that the parser, API behavior and database-related logic work as expected.


CI Validation

GitHub Actions is used to run automated validation on the repository.

This improves the reliability of the project and shows that changes can be tested automatically before being considered stable.


API Endpoints

MethodEndpointDescription
GET/Web dashboard with historical chart
GET/api/v1/dataFetches current exchange rate data and stores it
GET/api/v1/historyReturns stored historical exchange rate records

Example API Response

Example response from the exchange rate endpoint:

{
  "source": "SUNAT",
  "currency": "USD",
  "buy": 3.72,
  "sell": 3.75,
  "date": "2026-06-10"
}

Example response from the history endpoint:

[
  {
    "source_date": "2026-06-10",
    "buy": 3.72,
    "sell": 3.75
  },
  {
    "source_date": "2026-06-11",
    "buy": 3.73,
    "sell": 3.76
  }
]

The exact response depends on the available source data and the current database state.


Local Setup

Clone the repository:

git clone https://github.com/dopelDev/sunat-exchange.git
cd sunat-exchange

Create an environment file:

cp .env.example .env

Run the stack:

docker compose up --build

Open the application:

http://localhost:8000

Test the API:

curl http://localhost:8000/api/v1/data
curl http://localhost:8000/api/v1/history

Environment Variables

The project uses environment variables to configure the database and runtime behavior.

Example variables:

POSTGRES_DB=sunat_exchange
POSTGRES_USER=sunat
POSTGRES_PASSWORD=sunat
DATABASE_URL=postgresql://sunat:sunat@db:5432/sunat_exchange
HISTORY_DAYS=92
SCRAPE_DELAY_SECONDS=2.5
SCRAPE_RETRIES=2

Using environment variables keeps configuration separate from application code and makes the project easier to run in different environments.


Developer Workflow

Useful commands:

make test
make backup
make restore

The Makefile helps centralize common tasks such as validation, backup and restore operations.


Testing

Run the test suite with:

make test

Or directly with pytest:

pytest

The test suite is intended to validate:

  • API behavior.
  • Data parsing.
  • Database interactions.
  • Expected response formats.
  • Basic application reliability.

Backup and Restore

The project includes backup and restore workflows to make the database safer to operate.

This is important because a service that stores historical data should not only collect data, but also provide a way to recover it.

The backup workflow demonstrates operational thinking beyond basic application development.


Technical Decisions

Why Flask?

Flask was selected because the project needed a lightweight backend framework for a focused API and dashboard.

It keeps the codebase simple while still allowing clear routing, templates and JSON endpoints.


Why PostgreSQL?

PostgreSQL was used to make the project more realistic than a file-based or memory-only service.

It allows historical persistence, better data integrity and a deployment model closer to real backend applications.


Why Docker Compose?

Docker Compose was used to define the full stack in a reproducible way.

It allows the project to run with Nginx, Flask, PostgreSQL and cron services using a single command.


Why Cron?

Cron was used to separate scheduled data synchronization from normal API requests.

This makes the architecture cleaner and avoids depending only on manual endpoint calls to update data.


Why Chart.js?

Chart.js was used to provide a lightweight historical visualization without adding unnecessary frontend complexity.

It makes the stored data easier to understand visually.


Project Status

Current status: Portfolio-ready MVP

The project already demonstrates:

  • Backend API development.
  • External data ingestion.
  • PostgreSQL persistence.
  • Historical data visualization.
  • Docker Compose orchestration.
  • Scheduled synchronization.
  • Tests and CI validation.

The project can still be improved with a dedicated health endpoint, clearer API semantics and production deployment documentation.


Known Improvements

One design improvement planned for the API is to separate read operations from sync operations.

Current behavior:

GET /api/v1/data

Fetches current external data and stores it.

Planned behavior:

GET /api/v1/data/latest

Reads the latest stored value.

POST /api/v1/sync

Fetches new data and stores it.

This would make the API design cleaner and more aligned with REST conventions.


Lessons Learned

This project helped me practice:

  • Building backend services with Flask.
  • Working with external data sources.
  • Normalizing and storing financial data.
  • Designing Docker Compose stacks.
  • Running services behind Nginx.
  • Using PostgreSQL in a containerized application.
  • Creating scheduled jobs with cron.
  • Writing tests for backend behavior.
  • Setting up CI with GitHub Actions.
  • Thinking about backup and restore workflows.

Professional Value

This project strengthens my profile as a backend developer with DevOps fundamentals.

It is especially relevant for roles involving:

  • Backend development.
  • API development.
  • Python services.
  • PostgreSQL.
  • Docker Compose.
  • Linux services.
  • Scheduled jobs.
  • Nginx reverse proxy.
  • CI pipelines.
  • Application operations.

It shows that I can build a small but complete service that does more than return static data: it collects information, stores history, exposes an API, renders a dashboard and runs in a containerized environment.


Role

My role in this project:

  • Designed the backend application structure.
  • Implemented the Flask API.
  • Added PostgreSQL persistence.
  • Created the Docker Compose stack.
  • Configured Nginx as the public entry point.
  • Added cron-based synchronization.
  • Added historical visualization with Chart.js.
  • Created backup and restore workflows.
  • Added tests with pytest.
  • Configured GitHub Actions.
  • Wrote project documentation.

Repository

GitHub repository:

https://github.com/dopelDev/sunat-exchange


Short Description

Dockerized Flask application that tracks Peru’s SUNAT exchange rate, stores historical data in PostgreSQL, syncs daily with cron, exposes JSON endpoints and displays a Chart.js dashboard behind Nginx.


Tags

Python
Flask
PostgreSQL
Docker
Docker Compose
Nginx
Gunicorn
cron
Chart.js
pytest
GitHub Actions
Backend
DevOps
Exchange Rates
SUNAT
Peru