A full-stack web application that scrapes book data from World of Books and provides a beautiful interface for browsing and discovering books. Built with NestJS (backend) and Next.js (frontend).
- Features
- Architecture Overview
- Tech Stack
- Design Decisions
- Project Structure
- Prerequisites
- Installation
- Configuration
- Running the Application
- API Documentation
- Deployment
- Troubleshooting
- π Web Scraping: Automated scraping from World of Books using Playwright/Crawlee
- π Data Management: PostgreSQL database with TypeORM for structured data storage
- π Job Queue: Bull queue with Redis for asynchronous scraping tasks
- π Advanced Filtering: Search, category, price range, author, and rating filters
- π Pagination: Efficient data retrieval with pagination support
- π‘οΈ Rate Limiting: Throttler guard to prevent API abuse
- π API Documentation: Auto-generated Swagger/OpenAPI documentation
- β»οΈ Data Refresh: On-demand product data refresh from source
- π¨ Beautiful UI: Vintage book-themed design with amber/yellow aesthetic
- β‘ TanStack Query: Efficient data fetching with caching and background updates
- π Smart Search: Debounced search to prevent rate limit issues
- π― Advanced Filters: Category, price range, author filtering
- π± Responsive Design: Mobile-first approach with Tailwind CSS
- π Optimistic Updates: Smooth pagination without loading flashes
- π Book Details: Comprehensive product pages with specifications
- π Navigation: About and Contact pages with social links
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β FRONTEND β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Next.js 16 (React 19) + TypeScript + Tailwind CSS β β
β β - TanStack Query for data fetching β β
β β - Axios for HTTP requests β β
β β - React Hot Toast for notifications β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β HTTP/REST
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β BACKEND β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β NestJS API Server β β
β β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β Controllers (REST Endpoints) β β β
β β β - ProductController β β β
β β β - CategoryController β β β
β β β - ScraperController β β β
β β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β Services (Business Logic) β β β
β β β - ProductService β β β
β β β - ScraperService (Playwright/Crawlee) β β β
β β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β Bull Queue Processor β β β
β β β - ScraperProcessor (Async Jobs) β β β
β β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β β
ββββββββββββββββββββ ββββββββββββββββββββ ββββββββββββββββββββ
β PostgreSQL β β Redis β β World of Books β
β (Database) β β (Job Queue) β β (Web Scraping) β
β β β β β β
β - Products β β - Scrape Jobs β β - Product Data β
β - Categories β β - Queue Status β β - Images β
β - Reviews β β β β - Prices β
β - Navigation β β β β - Reviews β
ββββββββββββββββββββ ββββββββββββββββββββ ββββββββββββββββββββ
- User Request β Frontend sends request to backend API
- API Processing β NestJS controller validates and routes request
- Service Layer β Business logic processes the request
- Database Query β TypeORM fetches data from PostgreSQL
- Response β Data sent back to frontend with proper formatting
- Caching β TanStack Query caches response for 5 minutes
- Trigger β User or scheduler triggers scrape job
- Queue Job β ScraperController queues job in Bull/Redis
- Process β ScraperProcessor picks up job asynchronously
- Scrape β Playwright/Crawlee navigates and extracts data
- Store β Data saved to PostgreSQL via TypeORM
- Complete β Job marked as completed in queue
- Framework: NestJS 11
- Language: TypeScript 5.9
- Database: PostgreSQL (with TypeORM 0.3)
- Queue: Bull 4.16 + Redis (ioredis 5.9)
- Web Scraping: Playwright 1.57 + Crawlee 3.15
- Validation: class-validator + class-transformer
- API Docs: Swagger/OpenAPI (@nestjs/swagger)
- Rate Limiting: @nestjs/throttler
- Framework: Next.js 16.1 (App Router)
- UI Library: React 19.2
- Language: TypeScript 5
- Styling: Tailwind CSS 4
- Data Fetching: TanStack Query 5.90
- HTTP Client: Axios 1.13
- Icons: Lucide React 0.562
- Notifications: React Hot Toast 2.6
- Database: PostgreSQL 16+
- Cache/Queue: Redis 7+ (Upstash compatible)
- Container: Docker + Docker Compose
- Deployment: Vercel (Frontend) + Railway/Render (Backend)
- Why: Easier code sharing, unified versioning, and simplified deployment
- Structure: Separate
backend/andfrontend/directories with independent package.json files
- Why: Enterprise-grade architecture, built-in dependency injection, excellent TypeScript support
- Benefits: Modular design, testability, scalability, and extensive ecosystem
- Why: Modern React patterns, server components, improved performance
- Benefits: File-based routing, built-in optimization, SEO-friendly
- Why: Superior caching, background updates, optimistic UI
- Benefits: Reduced API calls, better UX, automatic refetching, placeholder data for pagination
- Why: Scraping is time-consuming and should be asynchronous
- Benefits: Non-blocking API, job retry logic, progress tracking, distributed processing
- Why: Modern web scraping with JavaScript rendering support
- Benefits: Handles dynamic content, screenshot debugging, retry logic, rate limiting
- Why: Type-safe database queries, migrations, relations
- Benefits: Auto-generated types, query builder, transaction support
- Why: Prevent rate limit (429) errors on text input
- Benefits: Reduced API calls, better server performance, improved UX
- Why: Better for distributed systems, no sequential ID leakage
- Benefits: Security, scalability, easier data migration
- Why: Unique, memorable design that reflects the book domain
- Benefits: Brand identity, aesthetic appeal, user engagement
Books Scraper/
βββ backend/ # NestJS Backend
β βββ src/
β β βββ config/ # Configuration files
β β β βββ database.config.ts
β β β βββ redis.config.ts
β β βββ modules/
β β β βββ product/ # Product module
β β β β βββ dto/ # Data Transfer Objects
β β β β βββ product.controller.ts
β β β β βββ product.service.ts
β β β β βββ product.module.ts
β β β βββ category/ # Category module
β β β βββ navigation/ # Navigation module
β β β β βββ entities/ # TypeORM entities
β β β βββ scraper/ # Scraper module
β β β βββ scraper.controller.ts
β β β βββ scraper.service.ts
β β β βββ scraper.processor.ts
β β β βββ scraper.module.ts
β β βββ app.module.ts # Root module
β β βββ main.ts # Application entry
β βββ storage/ # Crawlee storage
β βββ package.json
β βββ tsconfig.json
β
βββ frontend/ # Next.js Frontend
β βββ app/
β β βββ components/ # Reusable components
β β β βββ Navbar.tsx
β β β βββ Footer.tsx
β β β βββ ...
β β βββ pages/ # Page components
β β β βββ HomePage.tsx
β β β βββ BooksPage.tsx
β β β βββ BookDetailPage.tsx
β β βββ hooks/ # Custom React hooks
β β β βββ useProducts.ts
β β β βββ useCategories.ts
β β βββ lib/ # Utilities
β β β βββ api.ts # Axios instance
β β β βββ types.ts # TypeScript types
β β βββ about/ # About page
β β βββ contact/ # Contact page
β β βββ books/ # Books pages
β β βββ layout.tsx # Root layout
β β βββ page.tsx # Home page
β βββ package.json
β βββ tsconfig.json
β
βββ docker-compose.yml # Docker services
βββ seed-database.sh # Database seeding script
βββ README.md # This file
Before you begin, ensure you have the following installed:
- Node.js: v20.x or higher (Download)
- npm: v10.x or higher (comes with Node.js)
- PostgreSQL: v16.x or higher (Download)
- Redis: v7.x or higher (Download) or use Upstash
- Git: For cloning the repository
- Docker: For containerized deployment (Download)
- Docker Compose: For multi-container orchestration
git clone https://github.com/yourusername/books-scraper.git
cd books-scrapercd backend
npm install --legacy-peer-depsNote: We use
--legacy-peer-depsto handle peer dependency conflicts between NestJS packages and other dependencies.
cd ../frontend
npm install --legacy-peer-depsNote: Required for compatibility between React 19, Next.js 16, and TanStack Query 5.
cd ../backend
npx playwright installThis downloads Chromium, Firefox, and WebKit browsers needed for scraping.
Create a .env file in the backend/ directory:
# Server
PORT=8000
NODE_ENV=development
# Database (PostgreSQL)
DB_HOST=localhost
DB_PORT=5432
DB_USERNAME=postgres
DB_PASSWORD=your_password
DB_NAME=books_scraper
# Redis (Local or Upstash)
# Option 1: Local Redis
REDIS_HOST=localhost
REDIS_PORT=6379
# Option 2: Upstash Redis (recommended for production)
REDIS_URL=rediss://default:your_password@your-redis.upstash.io:6379
# Scraper
SCRAPER_TIMEOUT=120000
SCRAPER_MAX_CONCURRENCY=1
# API
API_PREFIX=api/v1
THROTTLE_TTL=60
THROTTLE_LIMIT=10Create a .env.local file in the frontend/ directory:
# Backend API URL
NEXT_PUBLIC_BACKEND_URL=http://localhost:8000- Create Database:
psql -U postgres
CREATE DATABASE books_scraper;
\q- Run Migrations (if applicable):
cd backend
npm run migration:runNote: TypeORM will auto-create tables on first run if
synchronize: trueis set in database config.
Option 1: Local Redis
# macOS
brew install redis
brew services start redis
# Ubuntu/Debian
sudo apt-get install redis-server
sudo systemctl start redis
# Windows
# Download from https://github.com/microsoftarchive/redis/releasesOption 2: Upstash (Recommended for Production)
- Sign up at upstash.com
- Create a Redis database
- Copy the
REDIS_URLto your.envfile
cd backend
npm run start:devBackend will run on http://localhost:8000
cd frontend
npm run devFrontend will run on http://localhost:3000
cd backend
npm run build
npm run start:prodcd frontend
npm run build
npm run start# Start all services (PostgreSQL, Redis, Backend, Frontend)
docker-compose up -d
# View logs
docker-compose logs -f
# Stop all services
docker-compose downOnce the backend is running, access the Swagger documentation at:
http://localhost:8000/api/docs
GET /api/v1/products- List products with filtersGET /api/v1/products/:id- Get product by IDPOST /api/v1/products/:id/refresh- Refresh product data
GET /api/v1/categories- List all categoriesGET /api/v1/categories/:id- Get category by ID
POST /api/v1/scraper/seed- Seed database with homepage productsPOST /api/v1/scraper/homepage- Scrape homepage productsPOST /api/v1/scraper/category?url=...- Scrape categoryPOST /api/v1/scraper/product?url=...- Scrape product details
# Get products with filters
curl "http://localhost:8000/api/v1/products?page=1&limit=20&search=Harry&minPrice=5&maxPrice=20"
# Seed database
curl -X POST http://localhost:8000/api/v1/scraper/seed
# Refresh product
curl -X POST http://localhost:8000/api/v1/products/{uuid}/refresh- Install Vercel CLI:
npm install -g vercel- Deploy:
cd frontend
vercel- Set Environment Variables in Vercel Dashboard:
NEXT_PUBLIC_BACKEND_URL=https://your-backend-url.com
- Production Deployment:
vercel --prod- Install Railway CLI:
npm install -g @railway/cli- Login:
railway login- Initialize Project:
cd backend
railway init- Add PostgreSQL:
railway add postgresql- Add Redis (or use Upstash):
railway add redis- Set Environment Variables:
railway variables set PORT=8000
railway variables set NODE_ENV=production
# Add all other variables from .env- Deploy:
railway up- Create
render.yamlin backend directory:
services:
- type: web
name: books-scraper-backend
env: node
buildCommand: npm install --legacy-peer-deps && npm run build
startCommand: npm run start:prod
envVars:
- key: NODE_ENV
value: production
- key: PORT
value: 8000
- key: DATABASE_URL
fromDatabase:
name: books-scraper-db
property: connectionString
- key: REDIS_URL
fromService:
name: books-scraper-redis
type: redis
property: connectionString
databases:
- name: books-scraper-db
databaseName: books_scraper
user: postgres
- name: books-scraper-redis
plan: starter-
Push to GitHub and connect to Render
-
Deploy via Render Dashboard
- Build Images:
docker-compose build- Push to Registry:
docker tag books-scraper-backend your-registry/books-scraper-backend
docker push your-registry/books-scraper-backend
docker tag books-scraper-frontend your-registry/books-scraper-frontend
docker push your-registry/books-scraper-frontend- Deploy to Cloud (AWS ECS, Google Cloud Run, Azure Container Instances)
# Solution: Use --legacy-peer-deps flag
npm install --legacy-peer-deps# Backend (port 8000)
lsof -ti:8000 | xargs kill -9
# Frontend (port 3000)
lsof -ti:3000 | xargs kill -9- Verify PostgreSQL is running:
pg_isready - Check credentials in
.env - Ensure database exists:
psql -U postgres -l
- Verify Redis is running:
redis-cli ping(should returnPONG) - Check REDIS_URL format for Upstash
- Test connection:
npm run test:redis(in backend)
cd backend
npx playwright install- Debouncing is implemented for search/author filters
- Wait 500ms between requests
- Check THROTTLE_LIMIT in backend
.env
- Ensure
NEXT_PUBLIC_BACKEND_URLis correctly set - Check backend CORS configuration in
main.ts
# Backend
cd backend
npm run build
# Frontend
cd frontend
npm run build- Increase
SCRAPER_TIMEOUTin.env - Check internet connection
- Verify World of Books website is accessible
- Restart development server after changing
.env - For frontend, variables must start with
NEXT_PUBLIC_ - Check
.envfile is in correct directory
npm run start:dev # Start development server
npm run start:prod # Start production server
npm run build # Build for production
npm run test # Run tests
npm run test:redis # Test Redis connection
npm run lint # Lint code
npm run format # Format code with Prettiernpm run dev # Start development server
npm run build # Build for production
npm run start # Start production server
npm run lint # Lint code# From project root
./seed-database.sh
# Or manually
curl -X POST http://localhost:8000/api/v1/scraper/seed- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Commit changes:
git commit -m 'Add amazing feature' - Push to branch:
git push origin feature/amazing-feature - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- World of Books - Data source
- NestJS - Backend framework
- Next.js - Frontend framework
- Playwright - Web scraping
- TanStack Query - Data fetching
For support, email harshitabarnwal2003@gmail.com or open an issue on GitHub.
Made with β€οΈ for book lovers