|
| 1 | +# Babysitter Catalog API |
| 2 | + |
| 3 | +A DDD-inspired CRUD microservice for managing babysitter profiles, built with |
| 4 | +**FastAPI**, **Beanie** (async ODM), and **MongoDB**. |
| 5 | + |
| 6 | +--- |
| 7 | + |
| 8 | +## Project structure |
| 9 | + |
| 10 | +``` |
| 11 | +sitters-catalog/ |
| 12 | +├── main.py # Root re-export (fastapi dev entry) |
| 13 | +├── pyproject.toml |
| 14 | +├── .env.example |
| 15 | +│ |
| 16 | +└── babysitter_catalog/ |
| 17 | + ├── main.py # App factory + lifespan |
| 18 | + ├── config.py # pydantic-settings |
| 19 | + │ |
| 20 | + ├── domain/ |
| 21 | + │ ├── babysitter.py # BabysitterDocument (Beanie model) |
| 22 | + │ └── value_objects.py # Location, ContactInfo, AvailabilitySlot |
| 23 | + │ |
| 24 | + ├── application/ |
| 25 | + │ ├── dtos.py # Create / Update / Response / Search DTOs |
| 26 | + │ └── babysitter_service.py # Use-case methods |
| 27 | + │ |
| 28 | + ├── infrastructure/ |
| 29 | + │ └── mongo_repository.py # Beanie query wrappers |
| 30 | + │ |
| 31 | + └── presentation/ |
| 32 | + └── babysitter_router.py # FastAPI APIRouter — all CRUD endpoints |
| 33 | +``` |
| 34 | + |
| 35 | +--- |
| 36 | + |
| 37 | +## Setup |
| 38 | + |
| 39 | +### 1. Prerequisites |
| 40 | + |
| 41 | +- Python 3.13+ |
| 42 | +- [uv](https://github.com/astral-sh/uv) (recommended) or pip |
| 43 | +- A running MongoDB instance (local or Atlas) |
| 44 | + |
| 45 | +### 2. Clone & install |
| 46 | + |
| 47 | +```bash |
| 48 | +git clone <repo-url> |
| 49 | +cd sitters-catalog |
| 50 | + |
| 51 | +# with uv |
| 52 | +uv sync |
| 53 | + |
| 54 | +# or with pip |
| 55 | +pip install -e . |
| 56 | +``` |
| 57 | + |
| 58 | +### 3. Configure environment |
| 59 | + |
| 60 | +```bash |
| 61 | +cp .env.example .env |
| 62 | +# edit .env and set MONGO_URI to your connection string |
| 63 | +``` |
| 64 | + |
| 65 | +--- |
| 66 | + |
| 67 | +## Running |
| 68 | + |
| 69 | +### Development (auto-reload) |
| 70 | + |
| 71 | +```bash |
| 72 | +fastapi dev |
| 73 | +# or explicitly: |
| 74 | +fastapi dev babysitter_catalog/main.py |
| 75 | +``` |
| 76 | + |
| 77 | +### Production |
| 78 | + |
| 79 | +```bash |
| 80 | +fastapi run |
| 81 | +# or with uvicorn directly: |
| 82 | +uvicorn babysitter_catalog.main:app --host 0.0.0.0 --port 8000 --workers 4 |
| 83 | +``` |
| 84 | + |
| 85 | +Interactive docs are available at `http://localhost:8000/docs`. |
| 86 | + |
| 87 | +--- |
| 88 | + |
| 89 | +## API endpoints |
| 90 | + |
| 91 | +| Method | Path | Description | |
| 92 | +|--------|-----------------------------------|------------------------------------| |
| 93 | +| GET | /health | Health check | |
| 94 | +| POST | /api/v1/babysitters/ | Create a babysitter | |
| 95 | +| GET | /api/v1/babysitters/ | List / search babysitters | |
| 96 | +| GET | /api/v1/babysitters/featured | Top 5 by experience | |
| 97 | +| GET | /api/v1/babysitters/{id} | Get by ID | |
| 98 | +| PUT | /api/v1/babysitters/{id} | Full update | |
| 99 | +| PATCH | /api/v1/babysitters/{id} | Partial update | |
| 100 | +| DELETE | /api/v1/babysitters/{id} | Hard delete | |
| 101 | +| POST | /api/v1/babysitters/{id}/deactivate | Soft-delete (is_active=False) | |
| 102 | + |
| 103 | +--- |
| 104 | + |
| 105 | +## Sample curl commands |
| 106 | + |
| 107 | +> Replace `BASE=http://localhost:8000` and `ID=<objectid>` as needed. |
| 108 | +
|
| 109 | +### Create a babysitter |
| 110 | + |
| 111 | +```bash |
| 112 | +curl -s -X POST "$BASE/api/v1/babysitters/" \ |
| 113 | + -H "Content-Type: application/json" \ |
| 114 | + -d '{ |
| 115 | + "first_name": "Alice", |
| 116 | + "last_name": "Dupont", |
| 117 | + "age": 28, |
| 118 | + "bio": "Experienced nanny with a love for creative play.", |
| 119 | + "hourly_rate": 18.50, |
| 120 | + "years_of_experience": 5, |
| 121 | + "languages": ["English", "French"], |
| 122 | + "certifications": ["First Aid", "CPR"], |
| 123 | + "availability": [ |
| 124 | + {"day": "Monday", "from_hour": 8, "to_hour": 18}, |
| 125 | + {"day": "Wednesday", "from_hour": 8, "to_hour": 18} |
| 126 | + ], |
| 127 | + "contact": {"email": "alice@example.com", "phone": "+33612345678"}, |
| 128 | + "location": {"city": "Paris", "country": "France", "latitude": 48.8566, "longitude": 2.3522} |
| 129 | + }' | jq |
| 130 | +``` |
| 131 | + |
| 132 | +### List all active babysitters (default page) |
| 133 | + |
| 134 | +```bash |
| 135 | +curl -s "$BASE/api/v1/babysitters/" | jq |
| 136 | +``` |
| 137 | + |
| 138 | +### Search with filters |
| 139 | + |
| 140 | +```bash |
| 141 | +curl -s "$BASE/api/v1/babysitters/?city=Paris&min_rate=15&language=French&limit=10" | jq |
| 142 | +``` |
| 143 | + |
| 144 | +### Get featured (top 5 by experience) |
| 145 | + |
| 146 | +```bash |
| 147 | +curl -s "$BASE/api/v1/babysitters/featured" | jq |
| 148 | +``` |
| 149 | + |
| 150 | +### Get by ID |
| 151 | + |
| 152 | +```bash |
| 153 | +curl -s "$BASE/api/v1/babysitters/$ID" | jq |
| 154 | +``` |
| 155 | + |
| 156 | +### Full update (PUT) |
| 157 | + |
| 158 | +```bash |
| 159 | +curl -s -X PUT "$BASE/api/v1/babysitters/$ID" \ |
| 160 | + -H "Content-Type: application/json" \ |
| 161 | + -d '{ |
| 162 | + "first_name": "Alice", |
| 163 | + "last_name": "Dupont", |
| 164 | + "age": 29, |
| 165 | + "hourly_rate": 20.00, |
| 166 | + "years_of_experience": 6, |
| 167 | + "languages": ["English", "French", "Spanish"], |
| 168 | + "certifications": ["First Aid"], |
| 169 | + "availability": [], |
| 170 | + "contact": {"email": "alice@example.com"}, |
| 171 | + "location": {"city": "Lyon", "country": "France"} |
| 172 | + }' | jq |
| 173 | +``` |
| 174 | + |
| 175 | +### Partial update (PATCH) |
| 176 | + |
| 177 | +```bash |
| 178 | +curl -s -X PATCH "$BASE/api/v1/babysitters/$ID" \ |
| 179 | + -H "Content-Type: application/json" \ |
| 180 | + -d '{"hourly_rate": 22.00, "bio": "Updated bio."}' | jq |
| 181 | +``` |
| 182 | + |
| 183 | +### Soft-delete (deactivate) |
| 184 | + |
| 185 | +```bash |
| 186 | +curl -s -X POST "$BASE/api/v1/babysitters/$ID/deactivate" | jq |
| 187 | +``` |
| 188 | + |
| 189 | +### Hard delete |
| 190 | + |
| 191 | +```bash |
| 192 | +curl -s -X DELETE "$BASE/api/v1/babysitters/$ID" -w "%{http_code}" |
| 193 | +# returns 204 No Content |
| 194 | +``` |
| 195 | + |
| 196 | +--- |
| 197 | + |
| 198 | +## MongoDB index recommendations |
| 199 | + |
| 200 | +Run these in `mongosh` (or via a migration script) for optimal query performance: |
| 201 | + |
| 202 | +```js |
| 203 | +use babysitter_catalog |
| 204 | + |
| 205 | +// Compound index for the most common search pattern |
| 206 | +db.babysitters.createIndex( |
| 207 | + { "location.city": 1, "hourly_rate": 1, "is_active": 1 }, |
| 208 | + { name: "idx_city_rate_active" } |
| 209 | +) |
| 210 | + |
| 211 | +// Language array index (multikey) |
| 212 | +db.babysitters.createIndex( |
| 213 | + { languages: 1 }, |
| 214 | + { name: "idx_languages" } |
| 215 | +) |
| 216 | + |
| 217 | +// Experience descending — powers the /featured endpoint |
| 218 | +db.babysitters.createIndex( |
| 219 | + { years_of_experience: -1, is_active: 1 }, |
| 220 | + { name: "idx_experience_active" } |
| 221 | +) |
| 222 | + |
| 223 | +// Unique email index |
| 224 | +db.babysitters.createIndex( |
| 225 | + { "contact.email": 1 }, |
| 226 | + { unique: true, name: "idx_email_unique" } |
| 227 | +) |
| 228 | + |
| 229 | +// Audit / time-range queries |
| 230 | +db.babysitters.createIndex( |
| 231 | + { created_at: -1 }, |
| 232 | + { name: "idx_created_at" } |
| 233 | +) |
| 234 | +``` |
0 commit comments