A full-stack web application for managing a library's books, members, and borrowing records. Built with FastAPI (backend) and Next.js (frontend).
| Layer | Technology |
|---|---|
| Backend | Python 3.11+, FastAPI, SQLAlchemy 2.0, Pydantic v2, Alembic |
| Frontend | Next.js 16 (App Router), React 19, TypeScript, Tailwind CSS |
| Database | PostgreSQL 14+ |
| Auth | JWT (access + refresh tokens), bcrypt password hashing |
| Testing | pytest (backend, 195 tests), Jest + React Testing Library (frontend, 228 tests) |
- Books — CRUD with ISBN validation, multi-author support, category tagging, copy tracking
- Members — CRUD with auto-generated library IDs (
LIBU0001, …), email uniqueness - Borrowing — Borrow / return flow with available-copy enforcement (DB trigger), overdue auto-detection
- Authors & Categories — Dedicated management with per-author borrowing statistics
- Dashboard — Summary stats, recent activity, most borrowed books
- Authentication — Login / logout with JWT access & refresh tokens, role-based access (admin / manager)
- Search & Sort — Server-driven sorting and full-text search across all list views
- Rate Limiting — Per-endpoint rate limits via SlowAPI
- Structured Logging — JSON logs with correlation IDs (
X-Request-ID)
| Tool | Version | Notes |
|---|---|---|
| Python | ≥ 3.11 | |
| Node.js | ≥ 18 | |
| PostgreSQL | ≥ 14 | Running and accessible locally |
git clone <repository-url>
cd numinolabs_assignmentCreate a PostgreSQL database:
createdb numinolabs_assignmentcd backend
# Create and activate a virtual environment
python -m venv .venv
source .venv/bin/activate # macOS / Linux
# .venv\Scripts\activate # Windows
# Install dependencies
pip install -r requirements.txt
# Configure environment
cp .env.example .envEdit backend/.env and set the required values:
DATABASE_URL=postgresql://user:password@localhost:5432/numinolabs_assignment
SECRET_KEY=<generate with: python -c "import secrets; print(secrets.token_hex(32))">Run database migrations and seed data:
alembic upgrade head # Apply all migrations
python -m seed # Seed 1000 books, 200 members, 2000+ borrowingsStart the API server:
uvicorn main:app --reload --port 8000The API is now available at http://localhost:8000. Interactive docs at http://localhost:8000/docs.
cd frontend
# Install dependencies
npm install
# Configure environment (default points to localhost:8000)
# Edit .env.local if your backend runs on a different host/port:
# NEXT_PUBLIC_API_URL=http://localhost:8000
# Start the dev server
npm run devThe app is now available at http://localhost:3000.
Use the seeded credentials to log in:
| Role | Password | |
|---|---|---|
| Admin | admin@library.com | admin123 |
| Manager | manager@library.com | manager123 |
These credentials are for development only.
cd backend
source .venv/bin/activate
# Run all tests
python -m pytest tests/ -q
# Run with verbose output
python -m pytest tests/ -v
# Run a specific test file
python -m pytest tests/test_books.py -v195 tests across 8 test modules covering all API endpoints, CRUD operations, RBAC, and edge cases.
cd frontend
# Run all tests
npm test
# Run in watch mode
npm run test:watch
# Run with coverage report
npm run test:coverage228 tests across 34 test suites covering pages, components, hooks, utilities, and API layer.
numinolabs_assignment/
├── backend/
│ ├── alembic/ # Database migration scripts
│ ├── api/
│ │ ├── deps.py # Shared dependencies (auth, pagination, DB session)
│ │ ├── exceptions.py # Custom HTTP exceptions
│ │ └── v1/
│ │ ├── router.py # Route registration
│ │ └── endpoints/ # Route handlers (books, members, borrows, …)
│ ├── config/
│ │ ├── database.py # SQLAlchemy engine & session setup
│ │ └── settings.py # Pydantic settings (env-driven)
│ ├── core/
│ │ ├── limiter.py # Rate limiter instance
│ │ ├── logging.py # Structured JSON logging
│ │ ├── redis_client.py # Optional Redis connection
│ │ └── security.py # JWT creation/verification, password hashing
│ ├── crud/ # Database query functions
│ ├── models/ # SQLAlchemy ORM models
│ ├── schemas/ # Pydantic request/response schemas
│ ├── tests/ # pytest test suite
│ ├── main.py # FastAPI app factory & middleware
│ ├── seed.py # Database seeding script
│ └── requirements.txt
│
├── frontend/
│ └── src/
│ ├── app/ # Next.js App Router pages
│ │ ├── books/ # Book list & detail pages
│ │ ├── members/ # Member list & detail pages
│ │ ├── borrowings/ # Borrowing list page
│ │ ├── dashboard/ # Dashboard with summary stats
│ │ ├── authors/ # Author detail page
│ │ └── login/ # Login page
│ ├── components/
│ │ ├── ui/ # Reusable UI primitives (Button, Dialog, Table, …)
│ │ ├── books/ # Book-specific components
│ │ ├── members/ # Member-specific components
│ │ ├── borrowings/ # Borrowing-specific components
│ │ └── layout/ # App shell, sidebar, auth guard
│ ├── hooks/ # Custom React hooks
│ ├── lib/
│ │ ├── api.ts # API client with token refresh
│ │ ├── queries/ # TanStack React Query hooks
│ │ ├── routes.ts # Centralized route constants
│ │ └── utils.ts # Shared utilities
│ ├── providers/ # React context providers (auth, query client)
│ └── middleware.ts # Next.js edge middleware (auth redirect)
│
└── README.md
All endpoints are prefixed with /api/v1. Protected routes require a Bearer token in the Authorization header.
| Method | Endpoint | Description | Auth |
|---|---|---|---|
POST |
/auth/login |
Obtain access + refresh tokens | No |
POST |
/auth/refresh |
Refresh an access token | No |
POST |
/auth/logout |
Revoke tokens | Yes |
GET |
/auth/me |
Current user profile | Yes |
GET |
/health |
Service health check | No |
GET |
/books/ |
List books (search, sort, filter) | Yes |
POST |
/books/ |
Create a book | Yes |
GET |
/books/{id} |
Get book details | Yes |
PATCH |
/books/{id} |
Update a book | Yes |
DELETE |
/books/{id} |
Delete a book | Yes |
GET |
/members/ |
List members | Yes |
POST |
/members/ |
Create a member | Yes |
GET |
/members/{id} |
Get member details | Yes |
PATCH |
/members/{id} |
Update a member | Yes |
DELETE |
/members/{id} |
Delete a member | Yes |
GET |
/borrows/ |
List borrowing records | Yes |
POST |
/borrows/ |
Borrow a book | Yes |
PATCH |
/borrows/{id}/return |
Return a borrowed book | Yes |
GET |
/authors/ |
List authors | Yes |
POST |
/authors/ |
Create an author | Yes |
GET |
/categories/ |
List categories | Yes |
POST |
/categories/ |
Create a category | Yes |
GET |
/dashboard/ |
Dashboard summary statistics | Yes |
Full interactive documentation is available at /docs (Swagger UI) when running in development mode.
| Variable | Required | Default | Description |
|---|---|---|---|
DATABASE_URL |
Yes | — | PostgreSQL connection string |
SECRET_KEY |
Yes | — | JWT signing key (min 32 chars) |
ENVIRONMENT |
No | development |
development / staging / production |
CORS_ORIGINS |
No | ["http://localhost:3000"] |
Allowed CORS origins |
ACCESS_TOKEN_EXPIRE_MINUTES |
No | 30 |
JWT access token lifetime |
REFRESH_TOKEN_EXPIRE_MINUTES |
No | 10080 (7 days) |
JWT refresh token lifetime |
REDIS_URL |
No | None |
Redis URL for token blocklist (recommended in production) |
LOG_LEVEL |
No | INFO |
Logging level |
| Variable | Required | Default | Description |
|---|---|---|---|
NEXT_PUBLIC_API_URL |
No | http://localhost:8000 |
Backend API URL |