The Bazaar Catalog API is a RESTful microservice responsible for managing the entire product lifecycle within the Bazaar ecosystem. Built on FastAPI and backed by PostgreSQL, it provides full CRUD operations for products, image management through Cloudinary, stock transaction handling, full-text search with relevance ranking, and asynchronous event processing via RabbitMQ. The service integrates with OpenTelemetry for distributed tracing in production environments.
- Core Features
- System Architecture
- Technology Stack
- Project Structure
- Environment Configuration
- Getting Started
- API Reference
- Testing
- Stress & Performance Testing (k6)
- Full Product Lifecycle Management:
- Create, read, update, and delete products with validation and ownership enforcement.
- Support for multi-image uploads per product with ordered display, powered by Cloudinary CDN.
- Paginated product listings with advanced filtering by category, price range, stock availability, and active status.
- Full-Text Search with Relevance Ranking:
- Leverages PostgreSQL
tsvectorwith weighted columns (title:A, description:B) for relevance-based search. - Trigram indexing (
pg_trgm) on product titles enables fuzzy matching and typo-tolerant queries. - Supports multiple sort orders: by creation date, price, or relevance score.
- Leverages PostgreSQL
- Stock & Transaction Processing:
- Exposes endpoints for stock deduction and restoration during order workflows.
- Subscribes to RabbitMQ events (
catalog.transactions.restore) to asynchronously restore inventory when orders are cancelled.
- Admin Backoffice Operations:
- Dedicated admin endpoints to list, filter, and moderate products across all sellers.
- Admins can block/unblock products, with a full state history audit trail tracking every status change.
- Seller Dashboard & Analytics:
- Sellers can view their own products with private filters (active, inactive, out-of-stock).
- Stats endpoint provides aggregated counts of total, active, and out-of-stock products per seller.
- Top-selling product rankings available both globally and per seller.
- Shareable Product Deep Links:
- Generates shareable URLs that redirect to the mobile app via custom URI scheme (
bazaar://product/{id}). - Gracefully handles unavailable products with a user-friendly fallback page.
- Generates shareable URLs that redirect to the mobile app via custom URI scheme (
- Inter-Service Communication:
- Validates seller status against the User Service before write operations (blocks requests from banned users).
- Provides cart-optimized product information endpoint for the Cart Service.
- Observability & Health Checks:
- Liveness (
/livez) and Readiness (/readyz) probes for container orchestration. - Readiness checks verify both database connectivity and Cloudinary availability.
- Full OpenTelemetry instrumentation in production and stress environments via
opentelemetry-instrument. - Structured logging with Loguru for consistent, queryable log output.
- Liveness (
The Catalog Service sits behind the Bazaar API Gateway and interacts with multiple services within the ecosystem:
graph TD
Gateway([API Gateway]) -->|"X-User-Id, X-User-Role"| CatalogService[Catalog Service - FastAPI]
subgraph Catalog Microservice
CatalogService -->|CRUD Operations| DB[(PostgreSQL 15)]
CatalogService -->|Image Upload / Delete| Cloudinary[Cloudinary CDN]
CatalogService -->|Validate Seller Status| UserService[User Service]
end
RabbitMQ[RabbitMQ] -->|"catalog.transactions.restore"| CatalogService
OrdersService([Orders Service]) -->|Publish Events| RabbitMQ
CartService([Cart Service]) -->|"GET /catalog/products/cart/info"| CatalogService
Note
The Catalog Service relies on trust-based authorization. It extracts user identity from the X-User-Id and X-User-Role headers injected by the API Gateway — it never handles JWT parsing directly.
| Layer | Technology |
|---|---|
| Framework | FastAPI 0.135.1 (Uvicorn ASGI server) |
| Language | Python 3.11 |
| Database | PostgreSQL 15, SQLAlchemy 2.0, Alembic (migrations) |
| Image Storage | Cloudinary (CDN-hosted product images) |
| Message Broker | RabbitMQ (via aio-pika, async AMQP) |
| Validation | Pydantic v2, pydantic-settings |
| Telemetry | OpenTelemetry SDK + FastAPI instrumentation |
| Logging | Loguru |
| Testing | Pytest, pytest-cov (≥ 70% coverage), pytest-timeout |
| Deployment | Docker, Docker Compose, bazaar-network |
| CI/CD | GitHub Actions (automated test pipeline) |
Create a .env file in the root directory based on the .env.example template:
cp .env.example .env| Variable | Description | Default / Example |
|---|---|---|
| Server | ||
PORT |
HTTP port the service listens on | 8081 |
HOST |
Bind address for the server | 0.0.0.0 |
ENVIRONMENT |
Runtime mode (development, testing, production, stress) |
development |
| Database | ||
DATABASE_HOST |
PostgreSQL hostname | postgres-catalog |
DATABASE_NAME |
Database name | api-catalog-db |
DATABASE_INNER_PORT |
Internal PostgreSQL port (container) | 5432 |
DATABASE_OUTER_PORT |
Mapped PostgreSQL port on host | 5432 |
DATABASE_USER |
Database user | api-catalog-user |
DATABASE_PASSWORD |
Database password | hard-to-guess-password |
| Test Database | ||
DATABASE_TEST_HOST |
Test PostgreSQL hostname | postgres-test |
DATABASE_TEST_NAME |
Test database name | api-catalog-test-db |
DATABASE_TEST_PORT |
Test PostgreSQL port | 5432 |
DATABASE_TEST_USER |
Test database user | api-catalog-test-user |
DATABASE_TEST_PASSWORD |
Test database password | hard-to-guess-password |
| Cloudinary | ||
CLOUDINARY_CLOUD_NAME |
Cloudinary cloud name for image hosting | cloudinary-name |
CLOUDINARY_API_KEY |
Cloudinary API key | your-cloudinary-api-key |
CLOUDINARY_API_SECRET |
Cloudinary API secret | your-cloudinary-api-secret |
| Inter-Service URLs | ||
API_USER_URL |
Base URL of the User Microservice | http://user-service:8080 |
API_ORDER_URL |
Base URL of the Orders Microservice | http://orders-service:8080 |
API_GATEWAY_URL |
Base URL of the API Gateway | http://localhost:8000 |
| Message Broker | ||
RABBIT_MQ_URL |
RabbitMQ connection string (AMQP) | amqps://guest:guest@localhost:5671 |
| Telemetry | ||
OTEL_SERVICE_NAME |
Service label reported in OpenTelemetry traces | api-catalog |
OTEL_EXPORTER_OTLP_PROTOCOL |
OTLP exporter protocol | http/protobuf |
OTEL_EXPORTER_OTLP_ENDPOINT |
OpenTelemetry collector endpoint | your-otlp-endpoint |
OTEL_EXPORTER_OTLP_HEADERS |
Authentication headers for the OTLP exporter | your-otel-headers |
| Deep Links | ||
MOBILE_APP_URL |
Custom URI scheme for mobile deep links | bazaar://product |
- Docker & Docker Compose
For local development without Docker:
- Python 3.11 or later
- pip (Python package manager)
- PostgreSQL 15
python -m venv venv
source venv/bin/activate # Linux / macOS
venv\Scripts\activate # Windows
pip install -r requirements.txt-
Create the shared Docker network (if not already created):
docker network create bazaar-network
-
Build and start the service:
docker compose up --build -d
The service starts on port
8081by default. On startup, Alembic automatically runs all pending database migrations. -
Verify the service is healthy:
curl http://localhost:8081/livez
Expected response:
{"status": "OK"}For a full readiness check (database + Cloudinary):
curl http://localhost:8081/readyz
Expected response:
{"status": "READY"}
To tear down the containers and remove volumes:
docker compose down -vTo follow the application logs in real time:
docker compose logs -f catalog-serviceInteractive API documentation (Swagger UI) is available once the application runs at:
- Swagger UI:
http://localhost:8081/docs#/
Or if you use the service in the cloud, it is available at:
- Swagger UI:
https://catalog-izb7.onrender.com/docs#/
The test suite uses Pytest with a minimum code coverage threshold of 70%. Tests run against an isolated PostgreSQL instance managed by Docker Compose.
The simplest way to run the full test suite:
chmod +x run_tests.sh # Only needed once
./run_tests.shThis script handles the full lifecycle: building the test containers, executing the tests with coverage reporting, and tearing down the environment.
If you prefer to manage the lifecycle yourself:
-
Start the test environment:
docker compose -f docker-compose.test.yml up --build -d
-
Execute the test suite:
docker exec fastapi_test_runner pytest -vv -s --timeout=2 --cov=src --cov-report=term-missing --cov-fail-under=70 -
Tear down the test environment:
docker compose -f docker-compose.test.yml down -v
The project includes a GitHub Actions workflow (.github/workflows/test.yml) that automatically runs the test suite on every push. The pipeline provisions a PostgreSQL service container, installs dependencies, and enforces the 70% coverage threshold.
To run k6 stress tests, the service must be started in stress mode:
-
Configure environment variables — set
ENVIRONMENT=stressin your.envfile. In stress mode, Cloudinary uploads are mocked for consistent benchmarking. -
Start the service:
docker compose up --build
-
Stop the service:
docker compose down -v
Important
Never use production credentials or databases for stress testing. If you encounter any issues, reach out to the team.