Skip to content

Repository files navigation

THIS REPO IS COMPLETELY MADE AND MAINTAINED BY CLAUDE CODE, AS AN ATTEMPT TO MAXIMIZE IT'S ABILITY TO DEVELOP.

Book Library API (TypeScript + Express)

A production-style demo REST API for a library catalog.

This project is intentionally designed to showcase backend engineering practices, not just CRUD logic. It demonstrates:

  • Layered architecture (Route -> Controller -> Service -> Repository)
  • Repository Pattern for data-access abstraction
  • Immutable data updates in the in-memory store
  • Input and environment validation with Zod
  • Security middleware (Helmet, rate limiting, JSON body limits)
  • Centralized error handling and consistent API response envelopes
  • Multi-level testing (unit, integration, e2e) with coverage thresholds

Table of Contents

What This Project Is

This is a backend API for managing book records in a library catalog.

You can:

  • Create books
  • List books (with pagination)
  • Get one book by id
  • Replace a book
  • Partially update a book
  • Delete a book
  • Check health status

What This Project Is Not

This is not an e-reader or digital book delivery service.

  • It does not store PDF/EPUB files
  • It does not render pages for reading
  • It does not include user accounts/authentication yet
  • It currently uses in-memory storage (data resets on restart)

Tech Stack

  • Runtime: Node.js
  • Language: TypeScript (strict mode)
  • Web framework: Express 5
  • Validation: Zod
  • Security middleware: Helmet, express-rate-limit
  • Logging: Winston
  • Tests: Jest + Supertest + ts-jest
  • Formatting and linting: Prettier + ESLint

Architecture

Request flow:

  1. Route receives HTTP request
  2. Controller validates input and maps request/response
  3. Service applies business rules
  4. Repository handles persistence operations
  5. Response helpers return a consistent envelope

Dependency direction:

  • Routes depend on Controllers
  • Controllers depend on Services
  • Services depend on Repository interface
  • Repository implementation can be swapped (in-memory today, DB tomorrow)

Project Structure

src/
  app.ts                      # Express app composition (middleware + routes)
  server.ts                   # Process entrypoint and graceful shutdown
  config/
    env.ts                    # Env schema + parser (Zod)
  controllers/
    book.controller.ts        # HTTP handlers for books
  lib/
    logger.ts                 # Winston logger factory
    response.ts               # Standard API response builders
    uuid.ts                   # UUID helper (if needed by future layers)
  middleware/
    request-logger.ts         # Request logging middleware
    not-found.ts              # 404 handler
    error-handler.ts          # Centralized error handling
  repositories/
    book.repository.ts        # Repository contract
    in-memory-book.repository.ts
  routes/
    health.route.ts
    book.route.ts
  schemas/
    book.schema.ts            # Request validation schemas
  services/
    book.service.ts           # Business logic + NotFound domain error
  types/
    book.ts
    api-response.ts
  __tests__/
    unit/
    integration/
    e2e/

Getting Started

Prerequisites

  • Node.js 20+ recommended
  • npm 10+ recommended

Install dependencies

npm install

Environment Variables

Copy .env.example to .env and adjust values if needed:

cp .env.example .env

.env.example contains:

PORT=3000
NODE_ENV=development
RATE_LIMIT_WINDOW_MS=60000
RATE_LIMIT_MAX=100
LOG_LEVEL=info

Variable details:

  • PORT: server port (required)
  • NODE_ENV: development | test | production
  • RATE_LIMIT_WINDOW_MS: rate limit window in milliseconds
  • RATE_LIMIT_MAX: max requests per IP in a window
  • LOG_LEVEL: Winston level (error, warn, info, debug)

Run the Application

Development mode (standard)

npm run dev

If your shell does not load .env automatically, run with explicit variables:

PORT=3000 NODE_ENV=development npm run dev

Build and run production output

npm run build
npm start

Health check

curl -sS http://localhost:3000/health

Expected response:

{
  "success": true,
  "data": {
    "status": "ok"
  },
  "error": null
}

Scripts

  • npm run dev: run server with ts-node
  • npm run build: compile TypeScript into dist/
  • npm start: run built server from dist/server.js
  • npm test: run Jest with coverage
  • npm run test:watch: run Jest in watch mode
  • npm run lint: lint TypeScript source files
  • npm run format: format TypeScript source files

API Reference

Base URL:

  • http://localhost:3000

Books base path:

  • /api/v1/books

1) Health

  • Method: GET
  • Path: /health
  • Success: 200

2) List Books

  • Method: GET
  • Path: /api/v1/books
  • Query params:
    • page (optional, integer >= 1)
    • limit (optional, integer >= 1)

Example:

curl -sS "http://localhost:3000/api/v1/books?page=1&limit=10"

Success response (200):

{
  "success": true,
  "data": [
    {
      "id": "2f2dc2af-4447-44df-a7a5-8e303de8fb74",
      "title": "Dune",
      "author": "Frank Herbert",
      "isbn": "9780441013593",
      "publishedYear": 1965,
      "genre": "Science Fiction",
      "createdAt": "2026-03-17T10:10:10.100Z",
      "updatedAt": "2026-03-17T10:10:10.100Z"
    }
  ],
  "error": null,
  "meta": {
    "total": 1,
    "page": 1,
    "limit": 10
  }
}

3) Get Book by ID

  • Method: GET
  • Path: /api/v1/books/:id

Example:

curl -sS http://localhost:3000/api/v1/books/<BOOK_ID>

Success response (200):

{
  "success": true,
  "data": {
    "id": "2f2dc2af-4447-44df-a7a5-8e303de8fb74",
    "title": "Dune",
    "author": "Frank Herbert",
    "isbn": "9780441013593",
    "publishedYear": 1965,
    "genre": "Science Fiction",
    "createdAt": "2026-03-17T10:10:10.100Z",
    "updatedAt": "2026-03-17T10:10:10.100Z"
  },
  "error": null
}

4) Create Book

  • Method: POST
  • Path: /api/v1/books
  • Body: JSON

Request body:

{
  "title": "Dune",
  "author": "Frank Herbert",
  "isbn": "9780441013593",
  "publishedYear": 1965,
  "genre": "Science Fiction"
}

Example:

curl -sS -X POST http://localhost:3000/api/v1/books \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Dune",
    "author": "Frank Herbert",
    "isbn": "9780441013593",
    "publishedYear": 1965,
    "genre": "Science Fiction"
  }'

Success response (201): same envelope as Get Book.

5) Replace Book

  • Method: PUT
  • Path: /api/v1/books/:id
  • Body: full book payload required

Example:

curl -sS -X PUT http://localhost:3000/api/v1/books/<BOOK_ID> \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Dune Messiah",
    "author": "Frank Herbert",
    "isbn": "9780441172696",
    "publishedYear": 1969,
    "genre": "Science Fiction"
  }'

Success: 200

6) Patch Book

  • Method: PATCH
  • Path: /api/v1/books/:id
  • Body: one or more optional fields

Example:

curl -sS -X PATCH http://localhost:3000/api/v1/books/<BOOK_ID> \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Dune (Updated)"
  }'

Success: 200

7) Delete Book

  • Method: DELETE
  • Path: /api/v1/books/:id

Example:

curl -i -X DELETE http://localhost:3000/api/v1/books/<BOOK_ID>

Success: 204 No Content

Validation Rules

Book payload rules:

  • title: string, min 1, max 200 chars
  • author: string, min 1, max 100 chars
  • isbn: string, exactly 13 digits (^\d{13}$)
  • publishedYear: integer between 1000 and current year
  • genre: one of:
    • Fiction
    • Non-Fiction
    • Science Fiction
    • Fantasy
    • Mystery
    • Thriller
    • Romance
    • Horror
    • Biography
    • History

Path rules:

  • id: must be a valid UUID

Query rules:

  • page: integer >= 1
  • limit: integer >= 1

Invalid input returns 422 with a message in the standard error envelope.

Response Format

The API uses a consistent envelope:

Success

{
  "success": true,
  "data": {},
  "error": null,
  "meta": {
    "total": 1,
    "page": 1,
    "limit": 10
  }
}

Error

{
  "success": false,
  "data": null,
  "error": "Human readable message"
}

Error Handling

Status codes used:

  • 200: successful read/update
  • 201: resource created
  • 204: resource deleted, no body
  • 404: route not found or book not found
  • 422: validation errors
  • 500: unexpected server error

Behavior:

  • Domain not-found errors are converted into 404 responses
  • Unknown errors are logged and returned as 500 Internal Server Error
  • Unmatched routes return 404 Not Found

Testing

Run full test suite with coverage

Use env vars so app initialization passes in tests:

PORT=3000 NODE_ENV=test npm test

Coverage threshold target (configured in jest.config.ts):

  • Lines: 80%
  • Branches: 80%
  • Functions: 80%
  • Statements: 80%

Current observed result on this repository state:

  • 9/9 suites passed
  • 173 tests passed
  • Global coverage above threshold

Watch mode

PORT=3000 NODE_ENV=test npm run test:watch

Current Known Caveats

  1. Build currently fails in strict TypeScript mode

At the moment, npm run build reports type errors related to exactOptionalPropertyTypes interactions in:

  • src/controllers/book.controller.ts
  • src/repositories/in-memory-book.repository.ts
  1. Workaround for running the app during development

If you hit those type-check issues in local dev, this command runs via ts-node transpile-only mode:

PORT=3000 NODE_ENV=development TS_NODE_TRANSPILE_ONLY=1 npm run dev
  1. In-memory persistence only
  • Data is lost whenever the process restarts
  • This is suitable for demos/tests, not production persistence

Security Features

Implemented safeguards:

  • Helmet security headers
  • Rate limiting with configurable window/max values
  • JSON payload limit (10kb)
  • Input validation at request boundaries
  • Structured error responses

Design Decisions

  • Repository interface separates business logic from storage implementation
  • Service layer throws domain-specific errors (NotFoundError)
  • Middleware pipeline keeps cross-cutting concerns isolated
  • Response helper utilities ensure API consistency
  • Immutable update patterns reduce accidental side effects

Roadmap Ideas

Possible improvements to evolve this into a production-ready service:

  1. Replace in-memory repository with PostgreSQL (or another persistent DB)
  2. Add authentication/authorization (JWT or session-based)
  3. Add OpenAPI/Swagger docs generation
  4. Add request id and structured tracing for observability
  5. Add pagination links and sorting/filtering support
  6. Add CI workflow for lint/test/build gates
  7. Add containerization (Dockerfile + compose profile)
  8. Fix strict TypeScript build issues and enforce build in CI

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages