Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Mini Bookstore Catalogue Service

A minimal yet production-grade Python service for managing a bookstore catalogue. Built with FastAPI, PostgreSQL, and Redis caching.

Features

  • RESTful API for book management (create, get, search)
  • PostgreSQL database with proper indexing
  • Multi-level caching (Redis + in-process LRU fallback)
  • Cursor-based pagination for scalability
  • Type-safe code with Pydantic and mypy
  • Comprehensive test suite
  • Docker and docker-compose support
  • Database migrations with Alembic
  • Structured logging
  • Prometheus metrics

Table of Contents

Installation

Prerequisites

  • Python: 3.11 or higher
  • PostgreSQL: 12 or higher
  • Redis: 6.0 or higher (required)
  • Docker & Docker Compose: (optional, for containerized deployment)

Step 1: Clone the Repository

git clone https://github.com/isivaselvan/mini-bookstore-svc.git
cd mini-bookstore-svc

Step 2: Create Virtual Environment

# Using make
make venv

# Or manually
python3 -m venv .venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate

Step 3: Install Dependencies

# Install with development dependencies
make install-dev

# Or manually
pip install -e ".[dev]"

Step 4: Set Up Environment Variables

Create a .env file in the project root:

# Create .env file manually or copy from example
cat > .env << EOF
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/bookstore
REDIS_URL=redis://localhost:6379
LOG_LEVEL=INFO
CACHE_TTL_BOOK=300
CACHE_TTL_SEARCH=60
EOF

Edit .env with your configuration (see Environment Variables section).

Step 5: Set Up Database

  1. Create PostgreSQL database:

    createdb bookstore
    # Or using psql:
    psql -U postgres -c "CREATE DATABASE bookstore;"
  2. Run migrations:

    make upgrade
    # Or manually:
    alembic upgrade head

Running Locally

Start Services

  1. Start PostgreSQL (if not running):

    # macOS (using Homebrew)
    brew services start postgresql
    
    # Linux (using systemd)
    sudo systemctl start postgresql
    
    # Or use Docker
    docker run -d --name postgres \
      -e POSTGRES_PASSWORD=postgres \
      -e POSTGRES_DB=bookstore \
      -p 5432:5432 \
      postgres:15-alpine
  2. Start Redis (if not running):

    # macOS (using Homebrew)
    brew services start redis
    
    # Linux (using systemd)
    sudo systemctl start redis
    
    # Or use Docker
    docker run -d --name redis \
      -p 6379:6379 \
      redis:7-alpine
  3. Start the API:

    make run
    # Or manually:
    bookstore-api
    # Or using uvicorn directly:
    uvicorn mini_bookstore_svc.main:app --reload --host 0.0.0.0 --port 8000

The API will be available at:

  • API: http://localhost:8000
  • API Docs (Swagger): http://localhost:8000/docs
  • API Docs (ReDoc): http://localhost:8000/redoc
  • Health Check: http://localhost:8000/healthz
  • Metrics: http://localhost:8000/metrics

Running with Docker

Using Docker Compose

  1. Start all services (API, PostgreSQL, Redis):

    make docker-up
    # Or manually:
    docker-compose -f docker/docker-compose.yml up -d
  2. Run database migrations:

    docker-compose -f docker/docker-compose.yml exec api alembic upgrade head
  3. Check service status:

    docker-compose -f docker/docker-compose.yml ps
  4. View logs:

    # All services
    docker-compose -f docker/docker-compose.yml logs -f
    
    # Specific service
    docker-compose -f docker/docker-compose.yml logs -f api
  5. Stop services:

    make docker-down
    # Or manually:
    docker-compose -f docker/docker-compose.yml down

Environment Variables

All configuration is done via environment variables. You can set them in a .env file or export them in your shell.

Required Variables

Variable Description Default
DATABASE_URL PostgreSQL connection string postgresql://postgres:postgres@localhost:5432/bookstore
REDIS_URL Redis connection URL redis://localhost:6379

Optional Variables

Variable Description Default
LOG_LEVEL Logging level (DEBUG, INFO, WARNING, ERROR) INFO
CACHE_TTL_BOOK TTL for book cache in seconds 300 (5 minutes)
CACHE_TTL_SEARCH TTL for search cache in seconds 60 (1 minute)
APP_NAME Application name Mini Bookstore Service
DEBUG Enable debug mode (boolean) False

Example .env File

# Database
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/bookstore

# Redis
REDIS_URL=redis://localhost:6379

# Logging
LOG_LEVEL=INFO

# Cache TTLs
CACHE_TTL_BOOK=300
CACHE_TTL_SEARCH=60

# Application
APP_NAME=Mini Bookstore Service
DEBUG=false

Connection String Formats

PostgreSQL:

postgresql://[user]:[password]@[host]:[port]/[database]

Redis:

redis://[host]:[port]/[db_number]
# Or with password:
redis://:[password]@[host]:[port]/[db_number]

Database Migrations

This project uses Alembic for database migrations.

Create a New Migration

make migrate MESSAGE="add_new_column"
# Or manually:
alembic revision --autogenerate -m "add_new_column"

Apply Migrations

make upgrade
# Or manually:
alembic upgrade head

Rollback Migrations

make downgrade
# Or manually:
alembic downgrade -1

View Migration History

alembic history

View Current Migration

alembic current

In Docker

# Apply migrations
docker-compose -f docker/docker-compose.yml exec api alembic upgrade head

# Create migration
docker-compose -f docker/docker-compose.yml exec api alembic revision --autogenerate -m "description"

# Rollback
docker-compose -f docker/docker-compose.yml exec api alembic downgrade -1

API Documentation

Base URL

  • Local: http://localhost:8000
  • Docker: http://localhost:8000

Interactive API Documentation

  • Swagger UI: http://localhost:8000/docs
  • ReDoc: http://localhost:8000/redoc

Endpoints

Method Endpoint Description
POST /v1/books Create a new book
GET /v1/books/{id} Get book by ID
GET /v1/books Search books with filters and pagination
GET /healthz Health check
GET /metrics Prometheus metrics
GET / API information

API Examples (cURL)

1. Create a Book

curl -X POST "http://localhost:8000/v1/books" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Python Programming",
    "author": "John Doe",
    "price": {
      "currency": "USD",
      "amount": 2999
    },
    "tags": ["python", "programming", "tech"],
    "published_at": "2024-01-15T10:00:00Z"
  }'

Response:

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "title": "Python Programming",
  "author": "John Doe",
  "price": {
    "currency": "USD",
    "amount": 2999
  },
  "tags": ["python", "programming", "tech"],
  "published_at": "2024-01-15T10:00:00Z",
  "created_at": "2024-01-20T12:00:00Z",
  "updated_at": "2024-01-20T12:00:00Z"
}

2. Get Book by ID

# Replace {id} with actual book ID from previous response
curl "http://localhost:8000/v1/books/550e8400-e29b-41d4-a716-446655440000"

Response:

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "title": "Python Programming",
  "author": "John Doe",
  "price": {
    "currency": "USD",
    "amount": 2999
  },
  "tags": ["python", "programming", "tech"],
  "published_at": "2024-01-15T10:00:00Z",
  "created_at": "2024-01-20T12:00:00Z",
  "updated_at": "2024-01-20T12:00:00Z"
}

3. Search Books (First Page)

curl "http://localhost:8000/v1/books?query=python&limit=10&sort=title:asc"

Response:

{
  "items": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "title": "Python Programming",
      "author": "John Doe",
      ...
    }
  ],
  "next": "eyJpZCI6IjU1MGU4NDAwLWUyOWItNDFkNC1hNzE2LTQ0NjY1NTQ0MDAwMCIsImxpbWl0IjoxMCwic29ydCI6InRpdGxlOmFzYyJ9"
}

4. Search Books with Tags Filter

curl "http://localhost:8000/v1/books?tag=tech&tag=programming&limit=10&sort=title:asc"

5. Search Books (Next Page - Using Cursor Token)

# Use the 'next' token from previous response
curl "http://localhost:8000/v1/books?page=eyJpZCI6IjU1MGU4NDAwLWUyOWItNDFkNC1hNzE2LTQ0NjY1NTQ0MDAwMCIsImxpbWl0IjoxMCwic29ydCI6InRpdGxlOmFzYyJ9&limit=10&sort=title:asc"

6. Search Books with Multiple Filters

curl "http://localhost:8000/v1/books?query=python&tag=tech&limit=5&sort=published_at:desc"

7. Health Check

curl "http://localhost:8000/healthz"

Response:

{
  "ok": true
}

8. Prometheus Metrics

curl "http://localhost:8000/metrics"

Response:

# HELP http_requests_total Total HTTP requests
# TYPE http_requests_total counter
http_requests_total{endpoint="/v1/books",method="GET",status="200"} 10.0
...

9. Get API Information

curl "http://localhost:8000/"

Response:

{
  "name": "Mini Bookstore Service",
  "version": "0.1.0",
  "docs": "/docs",
  "health": "/healthz"
}

Troubleshooting

Common Issues

1. Database Connection Error

Error:

sqlalchemy.exc.OperationalError: could not connect to server

Solutions:

  • Verify PostgreSQL is running: pg_isready or psql -U postgres
  • Check DATABASE_URL in .env file
  • Verify database exists: psql -U postgres -l | grep bookstore
  • Check firewall/network settings
  • For Docker: Ensure containers are on the same network

2. Redis Connection Error

Error:

redis.exceptions.ConnectionError: Error connecting to Redis

Solutions:

  • Verify Redis is running: redis-cli ping (should return PONG)
  • Check REDIS_URL in .env file
  • Verify Redis port (default: 6379) is not blocked
  • For Docker: Ensure containers are on the same network

3. Migration Errors

Error:

alembic.util.exc.CommandError: Target database is not up to date

Solutions:

  • Check current migration: alembic current
  • View migration history: alembic history
  • Apply pending migrations: alembic upgrade head
  • If needed, rollback: alembic downgrade -1

4. Port Already in Use

Error:

OSError: [Errno 48] Address already in use

Solutions:

  • Find process using port 8000: lsof -i :8000 (macOS/Linux)
  • Kill the process: kill -9 <PID>
  • Or use a different port: uvicorn mini_bookstore_svc.main:app --port 8001

Debugging Tips

  1. Enable Debug Logging:

    export LOG_LEVEL=DEBUG
    # Or in .env file:
    LOG_LEVEL=DEBUG
  2. Check Application Logs:

    # Local - logs are output to stdout/stderr
    # Docker
    docker-compose -f docker/docker-compose.yml logs -f api
  3. Test Database Connection:

    psql $DATABASE_URL -c "SELECT 1;"
  4. Test Redis Connection:

    redis-cli -u $REDIS_URL ping

Project Structure

mini-bookstore-svc/
├── src/
│   └── mini_bookstore_svc/
│       ├── __init__.py
│       ├── version.py
│       ├── config.py           # Configuration management
│       ├── logging.py          # Structured logging
│       ├── errors.py           # Custom exceptions
│       ├── models.py           # Pydantic models
│       ├── domain.py           # Business logic
│       ├── repo_sql.py         # Repository layer
│       ├── cache.py            # Caching layer
│       ├── api.py              # FastAPI routers
│       └── main.py             # Application factory
├── tests/
├── migrations/                 # Alembic migrations
├── docker/
│   ├── Dockerfile
│   └── docker-compose.yml
├── pyproject.toml
├── Makefile
└── README.md

Available Make Commands

make help              # Show all available commands
make venv              # Create virtual environment
make install           # Install production dependencies
make install-dev       # Install development dependencies
make run               # Run the application
make lint              # Run linter
make fmt               # Format code
make type-check        # Run type checker
make test              # Run tests
make test-cov          # Run tests with coverage
make migrate           # Create migration (use MESSAGE="desc")
make upgrade           # Apply migrations
make downgrade         # Rollback migration
make docker-build      # Build Docker image
make docker-up         # Start Docker services
make docker-down       # Stop Docker services

About

Bookstore Catalogue Service

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages