Skip to content

Commit 78ccbc1

Browse files
authored
Merge pull request #29 from PacktPublishing/sitters-catalog
chapter7 code
2 parents 30b0321 + c4ea762 commit 78ccbc1

54 files changed

Lines changed: 3386 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
MONGO_URI=mongodb://localhost:27017
2+
DB_NAME=babysitter_catalog
3+
APP_TITLE=Babysitter Catalog API
4+
DEBUG=false
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.13
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
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+
```
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from enum import StrEnum
2+
3+
from pydantic import ConfigDict
4+
from pydantic_settings import BaseSettings
5+
6+
7+
class RepositoryType(StrEnum):
8+
MONGO = "mongo"
9+
TINYDB_FILE = "tinydb_file"
10+
TINYDB_MEMORY = "tinydb_memory"
11+
12+
13+
class Settings(BaseSettings):
14+
mongo_uri: str = "mongodb://localhost:27017"
15+
db_name: str = "babysitter_catalog"
16+
app_title: str = "Babysitter Catalog API"
17+
debug: bool = False
18+
19+
# Repository configuration
20+
repository_type: RepositoryType = RepositoryType.TINYDB_FILE
21+
tinydb_path: str = "data/babysitters_db.json"
22+
23+
model_config = ConfigDict(env_file=".env")
24+
25+
26+
settings = Settings()

0 commit comments

Comments
 (0)