
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.
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.
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:
This makes the project more complete than a simple API script because it includes persistence, scheduled jobs, reverse proxy configuration, tests and containerized execution.
This project shows my ability to build and operate a small backend service with real infrastructure concerns.
Key skills demonstrated:
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:
sunat-exchange was created to solve those concerns in a small but realistic backend project.
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.
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
| Layer | Technology |
|---|---|
| Backend | Flask |
| WSGI Server | Gunicorn |
| Database | PostgreSQL |
| Reverse Proxy | Nginx |
| Scheduler | cron |
| Visualization | Chart.js |
| Containers | Docker |
| Orchestration | Docker Compose |
| Testing | pytest |
| CI | GitHub Actions |
| Automation | Makefile |
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.
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:
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 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.
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.
The project is designed to run with Docker Compose.
The stack includes separate services for:
nginxwebcrondbThis makes the project reproducible and easier to run in local or server environments.
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.
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.
| Method | Endpoint | Description |
|---|---|---|
GET | / | Web dashboard with historical chart |
GET | /api/v1/data | Fetches current exchange rate data and stores it |
GET | /api/v1/history | Returns stored historical exchange rate records |
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.
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
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.
Useful commands:
make test
make backup
make restore
The Makefile helps centralize common tasks such as validation, backup and restore operations.
Run the test suite with:
make test
Or directly with pytest:
pytest
The test suite is intended to validate:
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.
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.
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.
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.
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.
Chart.js was used to provide a lightweight historical visualization without adding unnecessary frontend complexity.
It makes the stored data easier to understand visually.
Current status: Portfolio-ready MVP
The project already demonstrates:
The project can still be improved with a dedicated health endpoint, clearer API semantics and production deployment documentation.
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.
This project helped me practice:
This project strengthens my profile as a backend developer with DevOps fundamentals.
It is especially relevant for roles involving:
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.
My role in this project:
GitHub repository:
https://github.com/dopelDev/sunat-exchange
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.
Python
Flask
PostgreSQL
Docker
Docker Compose
Nginx
Gunicorn
cron
Chart.js
pytest
GitHub Actions
Backend
DevOps
Exchange Rates
SUNAT
Peru