A modern, scalable backend system for airline ticket reservations built with Nest.js, Prisma ORM, and PostgreSQL.
- Nest.js - Progressive Node.js framework for building efficient server-side applications
- Prisma - Next-generation ORM for Node.js and TypeScript
- PostgreSQL - Open-source relational database
- Docker - Containerization platform for PostgreSQL
- TypeScript - Typed superset of JavaScript
- JWT - JSON Web Tokens for authentication
- Passport - Authentication middleware for Node.js
- bcrypt - Password hashing library
Before you begin, ensure you have the following installed:
- Node.js 20.x or higher
- npm or yarn
- Docker and Docker Compose
- Clone the repository:
git clone https://github.com/gmaxsoft/Airline_Ticket_Reservation_System.git
cd Airline_Ticket_Reservation_System- Install dependencies:
npm install- Create
.envfile from the example:
cp .env.example .env- Update the
.envfile with your configuration:
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/airline_reservation?schema=public"
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_DB=airline_reservation
POSTGRES_PORT=5432
JWT_SECRET="your-secret-key-here-change-in-production"Start PostgreSQL using Docker Compose:
npm run docker:upThis will start a PostgreSQL container with the following default configuration:
- User: postgres
- Password: postgres
- Database: airline_reservation
- Port: 5432
To stop the database:
npm run docker:downTo view database logs:
npm run docker:logsAfter setting up the database, generate Prisma Client and run migrations:
# Generate Prisma Client
npm run prisma:generate
# Create and apply migrations
npm run prisma:migrate
# Seed the database with sample data (48 users and 45 flights)
npm run prisma:seed
# Open Prisma Studio (database GUI)
npm run prisma:studioThe seed script creates:
- 48 sample users with hashed passwords (default password:
password123) - 45 sample flights with various destinations
The API uses JWT (JSON Web Tokens) for authentication. Protected endpoints require a valid JWT token in the Authorization header.
To obtain a JWT token, login using the /auth/login endpoint:
POST /auth/login
Content-Type: application/json
{
"email": "john.doe@example.com",
"password": "password123"
}Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": 1,
"email": "john.doe@example.com",
"fullName": "John Doe"
}
}Include the token in the Authorization header for protected endpoints:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...Token expiration: 1 day
All booking endpoints require authentication. Unauthenticated requests will receive a 401 Unauthorized response.
Login and obtain JWT token.
Request:
{
"email": "string (valid email)",
"password": "string (min 6 characters)"
}Response: 200 OK
{
"access_token": "string",
"user": {
"id": "number",
"email": "string",
"fullName": "string"
}
}Error: 401 Unauthorized - Invalid credentials
Create a new user.
Request:
{
"email": "string (valid email)",
"password": "string (min 6 characters)",
"fullName": "string"
}Response: 201 Created
{
"id": "number",
"email": "string",
"fullName": "string",
"createdAt": "ISO 8601 date"
}Error: 409 Conflict - Email already exists
Get all users (without passwords).
Response: 200 OK
[
{
"id": "number",
"email": "string",
"fullName": "string",
"createdAt": "ISO 8601 date"
}
]Get user by ID.
Response: 200 OK
{
"id": "number",
"email": "string",
"fullName": "string",
"createdAt": "ISO 8601 date"
}Error: 404 Not Found - User not found
Update user.
Request:
{
"email": "string (optional)",
"password": "string (optional, min 6 characters)",
"fullName": "string (optional)"
}Response: 200 OK
{
"id": "number",
"email": "string",
"fullName": "string",
"createdAt": "ISO 8601 date"
}Errors:
404 Not Found- User not found409 Conflict- Email already taken
Delete user.
Response: 204 No Content
Error: 404 Not Found - User not found
Create a new flight.
Request:
{
"flightNumber": "string (unique)",
"origin": "string (e.g., WAW)",
"destination": "string (e.g., JFK)",
"departureTime": "ISO 8601 date",
"price": "number (decimal)",
"totalSeats": "number (integer)"
}Response: 201 Created
{
"id": "number",
"flightNumber": "string",
"origin": "string",
"destination": "string",
"departureTime": "ISO 8601 date",
"price": "number",
"totalSeats": "number"
}Get all flights (ordered by departure time).
Response: 200 OK
[
{
"id": "number",
"flightNumber": "string",
"origin": "string",
"destination": "string",
"departureTime": "ISO 8601 date",
"price": "number",
"totalSeats": "number"
}
]Get flight by ID.
Response: 200 OK
{
"id": "number",
"flightNumber": "string",
"origin": "string",
"destination": "string",
"departureTime": "ISO 8601 date",
"price": "number",
"totalSeats": "number"
}Error: 404 Not Found - Flight not found
Update flight.
Request: (All fields optional)
{
"flightNumber": "string",
"origin": "string",
"destination": "string",
"departureTime": "ISO 8601 date",
"price": "number",
"totalSeats": "number"
}Response: 200 OK
Error: 404 Not Found - Flight not found
Delete flight.
Response: 204 No Content
Error: 404 Not Found - Flight not found
All booking endpoints require authentication. User ID is automatically extracted from JWT token.
Create a new booking. Automatically uses user ID from JWT token.
Headers:
Authorization: Bearer <token>
Request:
{
"flightId": "number (integer, min 1)",
"seatNumber": "string"
}Response: 201 Created
{
"id": "number",
"flightId": "number",
"userId": "number",
"seatNumber": "string",
"status": "confirmed",
"bookingDate": "ISO 8601 date",
"flight": {
"id": "number",
"flightNumber": "string",
"origin": "string",
"destination": "string",
"departureTime": "ISO 8601 date",
"price": "number",
"totalSeats": "number"
},
"user": {
"id": "number",
"email": "string",
"fullName": "string",
"createdAt": "ISO 8601 date"
}
}Business Validation:
- ✅ Checks if flight exists
- ✅ Checks if user exists
- ✅ Prevents overbooking (validates available seats)
- ✅ Prevents duplicate seat booking
- ✅ Prevents user from booking same flight twice
Errors:
401 Unauthorized- Missing or invalid token404 Not Found- Flight or user not found400 Bad Request- No available seats (overbooking)400 Bad Request- Seat already booked400 Bad Request- User already has booking for this flight
Get all bookings (admin view).
Headers:
Authorization: Bearer <token>
Response: 200 OK
[
{
"id": "number",
"flightId": "number",
"userId": "number",
"seatNumber": "string",
"status": "string",
"bookingDate": "ISO 8601 date",
"flight": { ... },
"user": { ... }
}
]Get logged-in user's bookings.
Headers:
Authorization: Bearer <token>
Response: 200 OK
[
{
"id": "number",
"flightId": "number",
"userId": "number",
"seatNumber": "string",
"status": "string",
"bookingDate": "ISO 8601 date",
"flight": {
"id": "number",
"flightNumber": "string",
"origin": "string",
"destination": "string",
"departureTime": "ISO 8601 date",
"price": "number",
"totalSeats": "number"
},
"user": {
"id": "number",
"email": "string",
"fullName": "string",
"createdAt": "ISO 8601 date"
}
}
]Note: Only returns bookings for the authenticated user (userId from JWT token).
Get booking by ID. Only owner can access their booking.
Headers:
Authorization: Bearer <token>
Response: 200 OK
{
"id": "number",
"flightId": "number",
"userId": "number",
"seatNumber": "string",
"status": "string",
"bookingDate": "ISO 8601 date",
"flight": { ... },
"user": { ... }
}Errors:
401 Unauthorized- Missing or invalid token404 Not Found- Booking not found403 Forbidden- User does not have permission to access this booking
Update booking. Only owner can update their booking.
Headers:
Authorization: Bearer <token>
Request:
{
"seatNumber": "string (optional)",
"status": "string (optional, 'confirmed' or 'cancelled')"
}Response: 200 OK
{
"id": "number",
"flightId": "number",
"userId": "number",
"seatNumber": "string",
"status": "string",
"bookingDate": "ISO 8601 date",
"flight": { ... },
"user": { ... }
}Business Rules:
- ✅ Can only cancel confirmed bookings
- ✅ Validates seat availability when changing seat number
- ✅ Prevents duplicate seat assignments
Errors:
401 Unauthorized- Missing or invalid token404 Not Found- Booking not found403 Forbidden- User does not own this booking400 Bad Request- Cannot cancel booking with status other than 'confirmed'400 Bad Request- New seat is already booked
Delete booking. Only owner can delete their booking.
Headers:
Authorization: Bearer <token>
Response: 204 No Content
Errors:
401 Unauthorized- Missing or invalid token404 Not Found- Booking not found403 Forbidden- User does not own this booking
# Development mode
npm run start:dev
# Production mode
npm run build
npm run start:prod
# Debug mode
npm run start:debugThe application will be available at http://localhost:3000.
# 1. Login and get JWT token
curl -X POST http://localhost:3000/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "john.doe@example.com",
"password": "password123"
}'
# Response:
# {
# "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
# "user": { "id": 1, "email": "john.doe@example.com", "fullName": "John Doe" }
# }
# 2. Get available flights
curl -X GET http://localhost:3000/flights
# 3. Create a booking
curl -X POST http://localhost:3000/bookings \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "Content-Type: application/json" \
-d '{
"flightId": 1,
"seatNumber": "12A"
}'
# 4. Get my bookings
curl -X GET http://localhost:3000/bookings/my \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
# 5. Cancel a booking
curl -X PATCH http://localhost:3000/bookings/1 \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "Content-Type: application/json" \
-d '{
"status": "cancelled"
}'Overbooking Protection:
# If all seats are booked, you'll get:
# 400 Bad Request: "No available seats for flight LO001. All 200 seats are already booked."Duplicate Seat Prevention:
# If seat is already booked:
# 400 Bad Request: "Seat 12A is already booked for this flight."Single Booking Per Flight:
# If user already has booking for this flight:
# 400 Bad Request: "You already have a confirmed booking for this flight."Access Control:
# If user tries to access another user's booking:
# 403 Forbidden: "You do not have permission to access this booking"# Unit tests
npm run test
# E2E tests
npm run test:e2e
# Test coverage
npm run test:cov
# Watch mode
npm run test:watchThe project includes comprehensive unit tests for:
- Users module - 28 tests covering CRUD operations and security
- Flights module - Tests for all flight endpoints
- Auth module - 35 tests covering authentication, JWT generation, Guard, and Strategy
- AuthService: 9 tests
- AuthController: 7 tests
- JwtStrategy: 12 tests
- JwtAuthGuard: 7 tests
- Bookings module - 42 tests covering business logic, validation, and access control
- BookingsService: 24 tests
- BookingsController: 18 tests
Total: Over 100 unit tests covering all modules
npm run build- Build the applicationnpm run start- Start the applicationnpm run start:dev- Start in development mode with watchnpm run start:prod- Start in production modenpm run start:debug- Start in debug mode
npm run lint- Run ESLintnpm run format- Format code with Prettier
npm run prisma:generate- Generate Prisma Clientnpm run prisma:migrate- Create and apply migrationsnpm run prisma:migrate:deploy- Deploy migrations to productionnpm run prisma:studio- Open Prisma Studio (database GUI)npm run prisma:format- Format Prisma schemanpm run prisma:seed- Seed database with sample data
npm run docker:up- Start Docker containersnpm run docker:down- Stop Docker containersnpm run docker:logs- View Docker logs
src/
├── auth/ # Authentication module
│ ├── auth.controller.ts
│ ├── auth.service.ts
│ ├── auth.module.ts
│ ├── jwt.strategy.ts # JWT strategy for Passport
│ ├── jwt-auth.guard.ts # JWT authentication guard
│ └── dto/
│ └── login.dto.ts
├── users/ # Users module
│ ├── users.controller.ts
│ ├── users.service.ts
│ ├── users.module.ts
│ └── dto/
│ ├── create-user.dto.ts
│ └── update-user.dto.ts
├── flights/ # Flights module
│ ├── flights.controller.ts
│ ├── flights.service.ts
│ ├── flights.module.ts
│ └── dto/
│ ├── create-flight.dto.ts
│ └── update-flight.dto.ts
├── bookings/ # Bookings module (protected)
│ ├── bookings.controller.ts
│ ├── bookings.service.ts
│ ├── bookings.module.ts
│ └── dto/
│ ├── create-booking.dto.ts
│ └── update-booking.dto.ts
├── prisma/ # Prisma service
│ ├── prisma.module.ts
│ └── prisma.service.ts
├── main.ts # Application entry point
└── app.module.ts # Root module
prisma/
├── schema.prisma # Database schema
├── seed.ts # Database seed script
├── migrations/ # Database migrations
└── config.ts # Prisma configuration
- Password Hashing - All passwords are hashed using bcrypt (10 salt rounds)
- JWT Authentication - Secure token-based authentication
- JWT Guard - Protected endpoints require valid JWT token
- Access Control - Users can only access/modify their own bookings
- Input Validation - All DTOs use class-validator for validation
- Password Security - Passwords are never returned in API responses
- Business Logic Validation - Prevents overbooking and duplicate bookings
This project uses:
- ESLint for code linting
- Prettier for code formatting
- Jest for testing
- GitHub Actions for CI/CD
- class-validator for DTO validation
- class-transformer for data transformation
- Nest.js Documentation
- Prisma Documentation
- PostgreSQL Documentation
- Passport.js Documentation
- JWT.io
- Create a feature branch
- Make your changes
- Run tests and linting
- Submit a pull request
This project is private and proprietary.