A microservices-based bookstore application built with NestJS, featuring separate services for users, books, and an API gateway for centralized access.
This project follows a microservices architecture pattern with the following components:
┌─────────────────────────────────────────────┐
│ API Gateway (HTTP) │
│ Port: 3001 │
└─────────────┬───────────────┬───────────────┘
│ │
│ TCP │ TCP
│ │
┌─────────▼─────┐ ┌────▼──────────┐
│ Users Service │ │ Books Service │
│ Port: 3002 │ │ Port: 3003 │
│ (TCP) │ │ (TCP) │
└───────────────┘ └───────────────┘
-
API Gateway (
apps/bookstore-api-gateway)- HTTP server that acts as the entry point for all client requests
- Routes requests to appropriate microservices
- Runs on port 3001
-
Users Microservice (
apps/users)- Handles user-related operations
- TCP-based microservice
- Runs on port 3002
-
Books Microservice (
apps/book)- Manages book inventory and operations
- TCP-based microservice
- Runs on port 3003
-
Shared Contracts (
libs/contracts)- Shared DTOs and interfaces across services
- Framework: NestJS 11.x
- Runtime: Node.js
- Language: TypeScript 5.x
- Transport: TCP (for microservices communication)
- Package Manager: npm
bookstore/
├── apps/
│ ├── bookstore-api-gateway/ # HTTP API Gateway
│ │ └── src/
│ │ ├── books/ # Books module
│ │ ├── users/ # Users module
│ │ ├── client-config/ # Microservice client configuration
│ │ └── main.ts
│ ├── book/ # Books microservice
│ │ └── src/
│ │ ├── book.controller.ts
│ │ ├── book.service.ts
│ │ └── main.ts
│ └── users/ # Users microservice
│ └── src/
│ ├── users.controller.ts
│ ├── users.service.ts
│ └── main.ts
├── libs/
│ └── contracts/ # Shared contracts/DTOs
├── .env # Environment configuration (not in git)
├── .env.example # Environment template
└── package.json
- Node.js (v18 or higher)
- npm (v9 or higher)
- Clone the repository:
git clone <repository-url>
cd bookstore- Install dependencies:
npm install- Set up environment variables:
cp .env.example .envThe .env file contains all port configurations:
# API Gateway
API_GATEWAY_PORT=3001
# Microservices
USERS_SERVICE_PORT=3002
BOOKS_SERVICE_PORT=3003
# Client Ports (used by API Gateway to connect to microservices)
USERS_CLIENT_PORT=3002
BOOKS_CLIENT_PORT=3003You can modify these ports as needed. If no .env file is present, the services will use these defaults.
You need to run all three services simultaneously. Open three separate terminal windows:
Terminal 1 - Users Microservice:
npm run start users -- --watchTerminal 2 - Books Microservice:
npm run start book -- --watchTerminal 3 - API Gateway:
npm run start bookstore-api-gateway -- --watchnpm run build
npm run start:prodYou should see console output indicating each service is running:
Users microservice is listening on port 3002Book microservice is listening on port 3003API Gateway is running on http://localhost:3001
All requests go through the API Gateway at http://localhost:3001
GET /usersExample:
curl http://localhost:3001/usersResponse:
[
{ "id": 1, "name": "John Doe" },
{ "id": 2, "name": "Jane Smith" }
]GET /booksExample:
curl http://localhost:3001/booksResponse:
[
{
"id": 1,
"title": "The Great Gatsby",
"author": "F. Scott Fitzgerald",
"rating": 5
},
{
"id": 2,
"title": "1984",
"author": "George Orwell",
"rating": 5
}
]GET /books/:idExample:
curl http://localhost:3001/books/1POST /books
Content-Type: application/jsonExample:
curl -X POST http://localhost:3001/books \
-H "Content-Type: application/json" \
-d '{
"title": "The Catcher in the Rye",
"author": "J.D. Salinger",
"rating": 4
}'PATCH /books/:id
Content-Type: application/jsonExample:
curl -X PATCH http://localhost:3001/books/1 \
-H "Content-Type: application/json" \
-d '{"rating": 5}'DELETE /books/:idExample:
curl -X DELETE http://localhost:3001/books/1# Unit tests
npm run test
# E2E tests
npm run test:e2e
# Test coverage
npm run test:cov- Generate a new NestJS application:
nest generate app <service-name>- Configure it as a microservice in
main.ts:
const app = await NestFactory.createMicroservice<MicroserviceOptions>(
ServiceModule,
{
transport: Transport.TCP,
options: {
port: parseInt(process.env.SERVICE_PORT || '3004', 10),
},
},
);- Add the service configuration to
.env:
SERVICE_PORT=3004
SERVICE_CLIENT_PORT=3004- Register the client in the API Gateway's
ClientConfigService
# Build all services
npm run build
# Format code
npm run format
# Lint code
npm run lint- Performance: TCP provides fast, reliable communication between services
- Simplicity: No need for message brokers or external dependencies
- Internal Communication: Services are not exposed directly to clients
- Single Entry Point: Clients only need to know one endpoint
- Security: Internal microservices are not directly accessible
- Flexibility: Easy to add authentication, rate limiting, etc.
- Service Discovery: Gateway handles routing to appropriate services
If you get "port already in use" errors:
- Find and kill the process:
# Find process using port 3001 (or 3002, 3003)
lsof -ti:3001
# Kill the process
kill -9 <PID>- Or change the port in
.envfile
This usually means:
- Microservices are not running
- Wrong port configuration in
.env - Microservice crashed
Solution: Ensure all three services (users, book, API gateway) are running
If you see "Can't resolve dependencies" errors:
- Ensure you're using Symbol constants for dependency injection tokens
- Check that providers are correctly registered in modules
This project is MIT licensed.