An open API service indexing awesome lists of open source software.

https://github.com/williamw-dev/travel-hub


https://github.com/williamw-dev/travel-hub

Last synced: about 1 month ago
JSON representation

Awesome Lists containing this project

README

          

# 🛬 Travel Hub

> A travel booking API with caching, real-time notifications, and recommendations.

![Archi](docs/archi.png)

## 🚀 Start Stack

- Use `docker-compose up` to start all services:

```bash
cd apps/server && pnpm i
cd ../client && pnpm i

cd ../.. && chmod +x docker/entrypoint.sh
cp .env.example .env
docker-compose up -d
```

## 📦 Setup & Environment

- Set up project with your chosen framework (FastAPI, Express, Spring Boot, etc.)
- Add support for environment variables
- Configure Docker and `docker-compose` to run:
- API service
- Redis
- MongoDB
- Neo4j

---

### ⚠️ Warnings

- Ensure you don't have redis or mongo running on your host machine, as the containers will use the same ports.

> MacOS

```sh
brew services stop redis
brew services stop mongodb-community
```

- On prometheus container, it's possible to have chmod permissions issues with the `entrypoint.sh` file. If you encounter this, run the following command to fix it:

```bash
chmod +x docker/entrypoint.sh
```

### 🗄️ Data Sources

- **MongoDB**: Store flight offers, hotels, and activities

```ts
use travelhub;

db.offers.insertMany([
{
from: "PAR",
to: "TYO",
departDate: ISODate("2025-07-10T08:00:00Z"),
returnDate: ISODate("2025-07-20T18:00:00Z"),
provider: "AirZen",
price: 750.0,
currency: "EUR",
legs: [
{ flightNum: "AZ123", dep: "CDG", arr: "HND", duration: "12h45" }
],
hotel: { name: "Tokyo Central Hotel", nights: 10, price: 400.0 },
activity: { title: "Mt. Fuji Day Tour", price: 120.0 }
},
{
from: "NYC",
to: "PAR",
departDate: ISODate("2025-07-15T12:00:00Z"),
returnDate: ISODate("2025-07-25T19:00:00Z"),
provider: "SkyWorld",
price: 540.0,
currency: "USD",
legs: [
{ flightNum: "SW456", dep: "JFK", arr: "CDG", duration: "7h10" }
],
hotel: null,
activity: null
},
{
from: "NYC",
to: "BER",
departDate: ISODate("2025-07-10T08:00:00Z"),
returnDate: ISODate("2025-07-20T18:00:00Z"),
provider: "SkyWorld",
price: 500,
currency: "USD",
legs: [{ flightNum: "SW100", dep: "JFK", arr: "BER", duration: "8h00" }],
hotel: null,
activity: null
}
])
```

- Neo4j cities and relationships:

```cypher
CREATE (par:City { code: "PAR", name: "Paris", country: "FR" });
CREATE (tyo:City { code: "TYO", name: "Tokyo", country: "JP" });
CREATE (nyc:City { code: "NYC", name: "New York", country: "US" });
CREATE (lon:City { code: "LON", name: "London", country: "UK" });

MATCH (par:City {code: "PAR"}), (lon:City {code: "LON"})
CREATE (par)-[:NEAR { weight: 0.9 }]->(lon);

MATCH (par:City {code: "PAR"}), (nyc:City {code: "NYC"})
CREATE (par)-[:NEAR { weight: 0.7 }]->(nyc);

MATCH (nyc:City {code: "NYC"}), (par:City {code: "PAR"})
CREATE (nyc)-[:NEAR { weight: 0.85 }]->(par);

MATCH (tyo:City {code: "TYO"}), (par:City {code: "PAR"})
CREATE (tyo)-[:NEAR { weight: 0.6 }]->(par);
```

---

### 🔌 Core Features

#### 1. `GET /offers`

- Implement Redis caching with TTL 60s: `offers::`
- On cache miss, query MongoDB (`from`, `to`, sort by `price` ascending)
- Store compressed JSON back into Redis
- Return array of offers with fields:
- `id`, `provider`, `price`, `currency`, `legs[]`, `hotel?`, `activity?`

#### 2. `GET /offers/{id}`

- Read from Redis cache (`offers:`, TTL 300s), fallback to MongoDB
- Return full offer details and `relatedOffers` (3 related IDs from Neo4j)

#### 3. `GET /reco`

- Accept query params: `city`, `k`
- Execute Cypher query in Neo4j:

```cypher
MATCH (c:City {code:$city})-[:NEAR]->(n:City)
RETURN n.code AS city
ORDER BY n.weight DESC
LIMIT $k
```

- Return list of city codes with scores

#### 4. `POST /login`

- Accept payload: `{ "userId": "u42" }`
- Generate UUID v4, store as `session: → userId` (EX 900s)
- Return: `{ "token": "", "expires_in": 900 }`

---

### 🔔 Real-Time Notifications

- Publish new offer notifications to Redis Pub/Sub channel `offers:new`
- Payload format:

```json
{
"offerId": "abc123",
"from": "PAR",
"to": "TYO"
}
```

---

### 📊 Indexes & Data Modeling

#### MongoDB

- Ensure `offers` collection schema:
- Fields: `_id`, `from`, `to`, `departDate`, `returnDate`, `provider`, `price`, `currency`, `legs`, `hotel`, `activity`
- Create indexes:
- `{ from: 1, to: 1, price: 1 }`
- Full-text index on `provider`

#### Neo4j

- Model nodes: `(c:City {code, name, country})`
- Model relationships: `(c1)-[:NEAR {weight}]->(c2)`

---

### 🚀 Non-functional Requirements

- Average latency for `/offers` should be 200ms (cache hit), 700ms max (cache miss)
- All responses should be `application/json; charset=utf-8`
- Log request durations and HTTP error codes

---

### ✨ Optional Features

- Text search support via `q=hotel` param using MongoDB text index
- `/stats/top-destinations` route using MongoDB aggregation + Redis caching
- `/metrics` route compatible with Prometheus (avg time, cache hit rate)