An asynchronous web application built with FastAPI, featuring a hybrid data storage architecture (PostgreSQL + Redis) to ensure instant redirects. A Telegram bot serves as the client interface. The project is fully containerized and ready for deployment in Kubernetes.
The project demonstrates how to build a high-performance and fault-tolerant web service using modern backend development practices.
- API with FastAPI: An asynchronous REST API for managing users and links.
- Hybrid Storage:
- PostgreSQL: Used as a reliable, long-term "Source of Truth" for user data and complete link information.
- Redis: Acts as the primary operational data store for "short code -> original URL" pairs, enabling redirects in microseconds. It is also used for atomic click counting and caching link lists.
- Background Task (APScheduler): Periodically synchronizes click counts from Redis to the main PostgreSQL database.
- Telegram Bot with Aiogram 3: A full-featured client for interacting with the API.
- Backend: Python 3.12, FastAPI, SQLAlchemy 2.0 (async), Pydantic V2, Alembic
- Databases: PostgreSQL, Redis
- Authentication: JWT (python-jose), OAuth2, passlib[bcrypt]
- Infrastructure & DevOps: Docker, Docker Compose, Kubernetes (K8s), CI/CD (GitHub Actions)
- Testing: Pytest, pytest-mock, httpx
- High Performance:
- Redis-First Architecture for Redirects: Redirect requests are handled directly from Redis without touching the main database, ensuring minimal latency.
- Asynchronous Click Counting: Uses the atomic
INCRoperation in Redis to avoid blocking the main thread. - API Response Caching: User link lists are cached to reduce the load on PostgreSQL.
- Reliability and Code Quality:
- Full Test Coverage: E2E tests for all endpoints and unit tests for caching logic using dependency overrides.
- Isolated Test Environment: Pytest is configured to work with an in-memory SQLite and a test Redis database.
- Automated Quality Assurance: A CI pipeline on GitHub Actions runs tests on every commit.
- Thoughtful Architecture:
- Resource Management via
lifespan: Correct initialization and closing of the Redis connection pool. - Cache Invalidation: The user's cache is automatically cleared when links are created, updated, or deleted.
- DB Migrations: Safe PostgreSQL schema management with Alembic.
- Resource Management via
- Docker
- Docker Compose
-
Clone the repository:
git clone https://github.com/Varenik-vkusny/Link-simplifier.git cd Link-simplifier -
Set up environment variables:
- Copy
.env.exampleto.envand.env.db.exampleto.env.db. - Fill in
BOT_TOKEN,SECRET_KEY, and other required variables.
- Copy
-
Run the application:
docker-compose up --build
-
Apply migrations (in a separate terminal):
- Wait for the containers to start, then run:
docker-compose exec web alembic upgrade head -
Done!
- The API is available at
http://localhost:8000 - Interactive API documentation:
http://localhost:8000/docs - Your Telegram bot is running and ready to use.
- The API is available at
docker-compose downThis section describes how to deploy the application in a local Kubernetes cluster, such as Minikube.
- kubectl: An installed and configured Kubernetes command-line client.
- Minikube: An installed local Kubernetes cluster.
- Nginx Ingress Controller: Installed in Minikube. It can be enabled with the command:
minikube addons enable ingress
All sensitive data must be created within the Kubernetes cluster. Run the following command to create the app-secrets secret. Replace the SECRET_KEY and BOT_TOKEN values with your own.
kubectl create secret generic app-secrets --namespace=link-simplifier \
--from-literal=DB_USER='Short' \
--from-literal=DB_PASSWORD='Link' \
--from-literal=SECRET_KEY='YOUR_OWN_SECRET_KEY_HERE' \
--from-literal=BOT_TOKEN='YOUR_TELEGRAM_BOT_TOKEN_HERE'-
Create the Namespace:
kubectl apply -f k8s/00-namespace.yaml
-
Apply all other manifests with a single command. The
migration-jobwill automatically apply migrations.kubectl apply -f k8s/
kubectl get all --namespace=link-simplifierYou should see running pods for api, bot, postgres, and redis. Ensure that the migration-job has a Completed status.
-
Get the direct access URL for the Ingress controller:
minikube service ingress-nginx-controller -n ingress-nginx --url
The command will output a URL, for example,
http://127.0.0.1:57135. Copy it. -
Test the API using
curl: Send a request using the obtained URL and the-H "Host: links.local"header.# Replace http://127.0.0.1:XXXXX with the URL from the previous step curl -v -H "Host: links.local" http://127.0.0.1:XXXXX/docs
Expected result: An
HTTP/1.1 200 OKresponse and the documentation HTML.
-
Check the Ingress Controller:
kubectl get pods -n ingress-nginx
- Expected result: The
ingress-nginx-controller-...pod should be in theRunningstatus.
- Expected result: The
-
Check your Ingress resource:
kubectl describe ingress ingress -n link-simplifier
- What to look for: The
Hostshould belinks.local, and theBackendssection should contain the IP addresses of yourapi-servicepods.
- What to look for: The
To delete all the Kubernetes resources created, simply delete the namespace:
kubectl delete namespace link-simplifier- Start Redis in the background:
docker-compose up -d redis
- Install dependencies and run tests:
pip install -r requirements.txt pytest
- Stop Redis after the tests:
docker-compose down