mdb-engine 0.2.4__tar.gz → 0.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. {mdb_engine-0.2.4/mdb_engine.egg-info → mdb_engine-0.3.1}/PKG-INFO +127 -23
  2. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/README.md +126 -22
  3. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/__init__.py +1 -1
  4. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/shared_middleware.py +18 -2
  5. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/core/engine.py +417 -5
  6. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/core/manifest.py +93 -0
  7. {mdb_engine-0.2.4 → mdb_engine-0.3.1/mdb_engine.egg-info}/PKG-INFO +127 -23
  8. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/pyproject.toml +1 -1
  9. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/setup.py +1 -1
  10. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/LICENSE +0 -0
  11. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/MANIFEST.in +0 -0
  12. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/README.md +0 -0
  13. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/ARCHITECTURE.md +0 -0
  14. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/README.md +0 -0
  15. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/__init__.py +0 -0
  16. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/audit.py +0 -0
  17. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/base.py +0 -0
  18. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/casbin_factory.py +0 -0
  19. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/casbin_models.py +0 -0
  20. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/config_defaults.py +0 -0
  21. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/config_helpers.py +0 -0
  22. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/cookie_utils.py +0 -0
  23. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/csrf.py +0 -0
  24. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/decorators.py +0 -0
  25. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/dependencies.py +0 -0
  26. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/helpers.py +0 -0
  27. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/integration.py +0 -0
  28. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/jwt.py +0 -0
  29. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/middleware.py +0 -0
  30. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/oso_factory.py +0 -0
  31. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/provider.py +0 -0
  32. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/rate_limiter.py +0 -0
  33. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/restrictions.py +0 -0
  34. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/session_manager.py +0 -0
  35. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/shared_users.py +0 -0
  36. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/token_lifecycle.py +0 -0
  37. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/token_store.py +0 -0
  38. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/users.py +0 -0
  39. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/auth/utils.py +0 -0
  40. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/cli/__init__.py +0 -0
  41. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/cli/commands/__init__.py +0 -0
  42. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/cli/commands/generate.py +0 -0
  43. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/cli/commands/migrate.py +0 -0
  44. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/cli/commands/show.py +0 -0
  45. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/cli/commands/validate.py +0 -0
  46. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/cli/main.py +0 -0
  47. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/cli/utils.py +0 -0
  48. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/config.py +0 -0
  49. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/constants.py +0 -0
  50. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/core/README.md +0 -0
  51. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/core/__init__.py +0 -0
  52. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/core/app_registration.py +0 -0
  53. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/core/app_secrets.py +0 -0
  54. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/core/connection.py +0 -0
  55. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/core/encryption.py +0 -0
  56. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/core/index_management.py +0 -0
  57. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/core/ray_integration.py +0 -0
  58. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/core/seeding.py +0 -0
  59. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/core/service_initialization.py +0 -0
  60. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/core/types.py +0 -0
  61. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/database/README.md +0 -0
  62. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/database/__init__.py +0 -0
  63. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/database/abstraction.py +0 -0
  64. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/database/connection.py +0 -0
  65. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/database/query_validator.py +0 -0
  66. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/database/resource_limiter.py +0 -0
  67. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/database/scoped_wrapper.py +0 -0
  68. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/dependencies.py +0 -0
  69. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/di/__init__.py +0 -0
  70. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/di/container.py +0 -0
  71. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/di/providers.py +0 -0
  72. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/di/scopes.py +0 -0
  73. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/embeddings/README.md +0 -0
  74. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/embeddings/__init__.py +0 -0
  75. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/embeddings/dependencies.py +0 -0
  76. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/embeddings/service.py +0 -0
  77. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/exceptions.py +0 -0
  78. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/indexes/README.md +0 -0
  79. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/indexes/__init__.py +0 -0
  80. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/indexes/helpers.py +0 -0
  81. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/indexes/manager.py +0 -0
  82. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/memory/README.md +0 -0
  83. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/memory/__init__.py +0 -0
  84. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/memory/service.py +0 -0
  85. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/observability/README.md +0 -0
  86. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/observability/__init__.py +0 -0
  87. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/observability/health.py +0 -0
  88. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/observability/logging.py +0 -0
  89. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/observability/metrics.py +0 -0
  90. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/repositories/__init__.py +0 -0
  91. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/repositories/base.py +0 -0
  92. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/repositories/mongo.py +0 -0
  93. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/repositories/unit_of_work.py +0 -0
  94. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/routing/README.md +0 -0
  95. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/routing/__init__.py +0 -0
  96. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/routing/websockets.py +0 -0
  97. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/utils/__init__.py +0 -0
  98. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine/utils/mongo.py +0 -0
  99. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine.egg-info/SOURCES.txt +0 -0
  100. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine.egg-info/dependency_links.txt +0 -0
  101. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine.egg-info/entry_points.txt +0 -0
  102. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine.egg-info/requires.txt +0 -0
  103. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/mdb_engine.egg-info/top_level.txt +0 -0
  104. {mdb_engine-0.2.4 → mdb_engine-0.3.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mdb-engine
3
- Version: 0.2.4
3
+ Version: 0.3.1
4
4
  Summary: MongoDB Engine
5
5
  Home-page: https://github.com/ranfysvalle02/mdb-engine
6
6
  Author: Fabian Valle
@@ -106,58 +106,162 @@ pip install mdb-engine
106
106
 
107
107
  ---
108
108
 
109
- ## 30-Second Quick Start
109
+ ## 30-Second Quick Start: Build a Todo List API
110
110
 
111
- **Step 1**: Create your `manifest.json`:
111
+ Let's build a complete CRUD todo list app in 3 steps!
112
+
113
+ ### Step 1: Create `manifest.json`
112
114
 
113
115
  ```json
114
116
  {
115
117
  "schema_version": "2.0",
116
- "slug": "my_app",
117
- "name": "My App",
118
+ "slug": "todo_app",
119
+ "name": "Todo List App",
118
120
  "managed_indexes": {
119
- "tasks": [
121
+ "todos": [
120
122
  {
121
123
  "type": "regular",
122
- "keys": {"status": 1, "created_at": -1},
123
- "name": "status_sort"
124
+ "keys": {"completed": 1, "created_at": -1},
125
+ "name": "completed_sort"
124
126
  }
125
127
  ]
126
128
  }
127
129
  }
128
130
  ```
129
131
 
130
- **Step 2**: Create your FastAPI app:
132
+ ### Step 2: Create `app.py` with Full CRUD
131
133
 
132
134
  ```python
135
+ from datetime import datetime
133
136
  from pathlib import Path
134
- from fastapi import Depends
137
+ from typing import Optional
138
+
139
+ from bson import ObjectId
140
+ from fastapi import Depends, HTTPException
141
+ from pydantic import BaseModel
142
+
135
143
  from mdb_engine import MongoDBEngine
136
144
  from mdb_engine.dependencies import get_scoped_db
137
145
 
138
- # Initialize the engine
146
+ # Initialize engine
139
147
  engine = MongoDBEngine(
140
148
  mongo_uri="mongodb://localhost:27017",
141
149
  db_name="my_database"
142
150
  )
143
151
 
144
- # Create app - manifest.json is loaded automatically!
145
- app = engine.create_app(slug="my_app", manifest=Path("manifest.json"))
152
+ # Create app - manifest.json loaded automatically!
153
+ app = engine.create_app(
154
+ slug="todo_app",
155
+ manifest=Path("manifest.json")
156
+ )
146
157
 
147
- # Use request-scoped dependencies - all queries automatically isolated
148
- @app.post("/tasks")
149
- async def create_task(task: dict, db=Depends(get_scoped_db)):
150
- result = await db.tasks.insert_one(task)
151
- return {"id": str(result.inserted_id)}
158
+ # Pydantic models
159
+ class TodoCreate(BaseModel):
160
+ title: str
161
+ description: Optional[str] = None
162
+
163
+ class TodoUpdate(BaseModel):
164
+ title: Optional[str] = None
165
+ description: Optional[str] = None
166
+ completed: Optional[bool] = None
167
+
168
+ # CREATE - Add a new todo
169
+ @app.post("/todos")
170
+ async def create_todo(todo: TodoCreate, db=Depends(get_scoped_db)):
171
+ doc = {
172
+ **todo.dict(),
173
+ "completed": False,
174
+ "created_at": datetime.utcnow()
175
+ }
176
+ result = await db.todos.insert_one(doc)
177
+ return {"id": str(result.inserted_id), "message": "Todo created"}
178
+
179
+ # READ - List all todos
180
+ @app.get("/todos")
181
+ async def list_todos(completed: Optional[bool] = None, db=Depends(get_scoped_db)):
182
+ query = {}
183
+ if completed is not None:
184
+ query["completed"] = completed
185
+
186
+ todos = await db.todos.find(query).sort("created_at", -1).to_list(length=100)
187
+ for todo in todos:
188
+ todo["_id"] = str(todo["_id"])
189
+ return {"todos": todos, "count": len(todos)}
190
+
191
+ # READ - Get single todo
192
+ @app.get("/todos/{todo_id}")
193
+ async def get_todo(todo_id: str, db=Depends(get_scoped_db)):
194
+ todo = await db.todos.find_one({"_id": ObjectId(todo_id)})
195
+ if not todo:
196
+ raise HTTPException(status_code=404, detail="Todo not found")
197
+ todo["_id"] = str(todo["_id"])
198
+ return todo
199
+
200
+ # UPDATE - Update a todo
201
+ @app.put("/todos/{todo_id}")
202
+ async def update_todo(todo_id: str, todo: TodoUpdate, db=Depends(get_scoped_db)):
203
+ updates = {k: v for k, v in todo.dict(exclude_unset=True).items() if v is not None}
204
+ if not updates:
205
+ raise HTTPException(status_code=400, detail="No fields to update")
206
+
207
+ updates["updated_at"] = datetime.utcnow()
208
+ result = await db.todos.update_one(
209
+ {"_id": ObjectId(todo_id)},
210
+ {"$set": updates}
211
+ )
212
+
213
+ if result.matched_count == 0:
214
+ raise HTTPException(status_code=404, detail="Todo not found")
215
+ return {"message": "Todo updated"}
216
+
217
+ # DELETE - Delete a todo
218
+ @app.delete("/todos/{todo_id}")
219
+ async def delete_todo(todo_id: str, db=Depends(get_scoped_db)):
220
+ result = await db.todos.delete_one({"_id": ObjectId(todo_id)})
221
+ if result.deleted_count == 0:
222
+ raise HTTPException(status_code=404, detail="Todo not found")
223
+ return {"message": "Todo deleted"}
224
+ ```
225
+
226
+ ### Step 3: Run It!
227
+
228
+ ```bash
229
+ # Start MongoDB (if not running)
230
+ mongod
231
+
232
+ # Install dependencies
233
+ pip install mdb-engine fastapi uvicorn
234
+
235
+ # Run the app
236
+ uvicorn app:app --reload
237
+ ```
238
+
239
+ **Test your API:**
240
+ ```bash
241
+ # Create a todo
242
+ curl -X POST http://localhost:8000/todos \
243
+ -H "Content-Type: application/json" \
244
+ -d '{"title": "Buy groceries", "description": "Milk and eggs"}'
245
+
246
+ # List todos
247
+ curl http://localhost:8000/todos
248
+
249
+ # Update a todo (replace {id} with actual ID)
250
+ curl -X PUT http://localhost:8000/todos/{id} \
251
+ -H "Content-Type: application/json" \
252
+ -d '{"completed": true}'
253
+
254
+ # Delete a todo
255
+ curl -X DELETE http://localhost:8000/todos/{id}
152
256
  ```
153
257
 
154
258
  **What just happened?**
155
- - ✅ Engine loaded your `manifest.json`
156
- - ✅ Indexes created automatically from `managed_indexes`
157
- - ✅ Database queries automatically scoped to your app
158
- - ✅ Lifecycle management handled (startup/shutdown)
259
+ - ✅ **Automatic scoping**: All queries filtered by `app_id` — your data is isolated
260
+ - ✅ **Indexes created**: The `completed_sort` index was created automatically
261
+ - ✅ **Lifecycle managed**: Startup/shutdown handled automatically
262
+ - ✅ **Zero boilerplate**: No connection setup, no index scripts, no auth handlers
159
263
 
160
- That's it. Your data is automatically sandboxed, indexes are created, and cleanup is handled.
264
+ **That's it!** You now have a fully functional, production-ready todo API with automatic data sandboxing, index management, and lifecycle handling.
161
265
 
162
266
  ---
163
267
 
@@ -41,58 +41,162 @@ pip install mdb-engine
41
41
 
42
42
  ---
43
43
 
44
- ## 30-Second Quick Start
44
+ ## 30-Second Quick Start: Build a Todo List API
45
45
 
46
- **Step 1**: Create your `manifest.json`:
46
+ Let's build a complete CRUD todo list app in 3 steps!
47
+
48
+ ### Step 1: Create `manifest.json`
47
49
 
48
50
  ```json
49
51
  {
50
52
  "schema_version": "2.0",
51
- "slug": "my_app",
52
- "name": "My App",
53
+ "slug": "todo_app",
54
+ "name": "Todo List App",
53
55
  "managed_indexes": {
54
- "tasks": [
56
+ "todos": [
55
57
  {
56
58
  "type": "regular",
57
- "keys": {"status": 1, "created_at": -1},
58
- "name": "status_sort"
59
+ "keys": {"completed": 1, "created_at": -1},
60
+ "name": "completed_sort"
59
61
  }
60
62
  ]
61
63
  }
62
64
  }
63
65
  ```
64
66
 
65
- **Step 2**: Create your FastAPI app:
67
+ ### Step 2: Create `app.py` with Full CRUD
66
68
 
67
69
  ```python
70
+ from datetime import datetime
68
71
  from pathlib import Path
69
- from fastapi import Depends
72
+ from typing import Optional
73
+
74
+ from bson import ObjectId
75
+ from fastapi import Depends, HTTPException
76
+ from pydantic import BaseModel
77
+
70
78
  from mdb_engine import MongoDBEngine
71
79
  from mdb_engine.dependencies import get_scoped_db
72
80
 
73
- # Initialize the engine
81
+ # Initialize engine
74
82
  engine = MongoDBEngine(
75
83
  mongo_uri="mongodb://localhost:27017",
76
84
  db_name="my_database"
77
85
  )
78
86
 
79
- # Create app - manifest.json is loaded automatically!
80
- app = engine.create_app(slug="my_app", manifest=Path("manifest.json"))
87
+ # Create app - manifest.json loaded automatically!
88
+ app = engine.create_app(
89
+ slug="todo_app",
90
+ manifest=Path("manifest.json")
91
+ )
81
92
 
82
- # Use request-scoped dependencies - all queries automatically isolated
83
- @app.post("/tasks")
84
- async def create_task(task: dict, db=Depends(get_scoped_db)):
85
- result = await db.tasks.insert_one(task)
86
- return {"id": str(result.inserted_id)}
93
+ # Pydantic models
94
+ class TodoCreate(BaseModel):
95
+ title: str
96
+ description: Optional[str] = None
97
+
98
+ class TodoUpdate(BaseModel):
99
+ title: Optional[str] = None
100
+ description: Optional[str] = None
101
+ completed: Optional[bool] = None
102
+
103
+ # CREATE - Add a new todo
104
+ @app.post("/todos")
105
+ async def create_todo(todo: TodoCreate, db=Depends(get_scoped_db)):
106
+ doc = {
107
+ **todo.dict(),
108
+ "completed": False,
109
+ "created_at": datetime.utcnow()
110
+ }
111
+ result = await db.todos.insert_one(doc)
112
+ return {"id": str(result.inserted_id), "message": "Todo created"}
113
+
114
+ # READ - List all todos
115
+ @app.get("/todos")
116
+ async def list_todos(completed: Optional[bool] = None, db=Depends(get_scoped_db)):
117
+ query = {}
118
+ if completed is not None:
119
+ query["completed"] = completed
120
+
121
+ todos = await db.todos.find(query).sort("created_at", -1).to_list(length=100)
122
+ for todo in todos:
123
+ todo["_id"] = str(todo["_id"])
124
+ return {"todos": todos, "count": len(todos)}
125
+
126
+ # READ - Get single todo
127
+ @app.get("/todos/{todo_id}")
128
+ async def get_todo(todo_id: str, db=Depends(get_scoped_db)):
129
+ todo = await db.todos.find_one({"_id": ObjectId(todo_id)})
130
+ if not todo:
131
+ raise HTTPException(status_code=404, detail="Todo not found")
132
+ todo["_id"] = str(todo["_id"])
133
+ return todo
134
+
135
+ # UPDATE - Update a todo
136
+ @app.put("/todos/{todo_id}")
137
+ async def update_todo(todo_id: str, todo: TodoUpdate, db=Depends(get_scoped_db)):
138
+ updates = {k: v for k, v in todo.dict(exclude_unset=True).items() if v is not None}
139
+ if not updates:
140
+ raise HTTPException(status_code=400, detail="No fields to update")
141
+
142
+ updates["updated_at"] = datetime.utcnow()
143
+ result = await db.todos.update_one(
144
+ {"_id": ObjectId(todo_id)},
145
+ {"$set": updates}
146
+ )
147
+
148
+ if result.matched_count == 0:
149
+ raise HTTPException(status_code=404, detail="Todo not found")
150
+ return {"message": "Todo updated"}
151
+
152
+ # DELETE - Delete a todo
153
+ @app.delete("/todos/{todo_id}")
154
+ async def delete_todo(todo_id: str, db=Depends(get_scoped_db)):
155
+ result = await db.todos.delete_one({"_id": ObjectId(todo_id)})
156
+ if result.deleted_count == 0:
157
+ raise HTTPException(status_code=404, detail="Todo not found")
158
+ return {"message": "Todo deleted"}
159
+ ```
160
+
161
+ ### Step 3: Run It!
162
+
163
+ ```bash
164
+ # Start MongoDB (if not running)
165
+ mongod
166
+
167
+ # Install dependencies
168
+ pip install mdb-engine fastapi uvicorn
169
+
170
+ # Run the app
171
+ uvicorn app:app --reload
172
+ ```
173
+
174
+ **Test your API:**
175
+ ```bash
176
+ # Create a todo
177
+ curl -X POST http://localhost:8000/todos \
178
+ -H "Content-Type: application/json" \
179
+ -d '{"title": "Buy groceries", "description": "Milk and eggs"}'
180
+
181
+ # List todos
182
+ curl http://localhost:8000/todos
183
+
184
+ # Update a todo (replace {id} with actual ID)
185
+ curl -X PUT http://localhost:8000/todos/{id} \
186
+ -H "Content-Type: application/json" \
187
+ -d '{"completed": true}'
188
+
189
+ # Delete a todo
190
+ curl -X DELETE http://localhost:8000/todos/{id}
87
191
  ```
88
192
 
89
193
  **What just happened?**
90
- - ✅ Engine loaded your `manifest.json`
91
- - ✅ Indexes created automatically from `managed_indexes`
92
- - ✅ Database queries automatically scoped to your app
93
- - ✅ Lifecycle management handled (startup/shutdown)
194
+ - ✅ **Automatic scoping**: All queries filtered by `app_id` — your data is isolated
195
+ - ✅ **Indexes created**: The `completed_sort` index was created automatically
196
+ - ✅ **Lifecycle managed**: Startup/shutdown handled automatically
197
+ - ✅ **Zero boilerplate**: No connection setup, no index scripts, no auth handlers
94
198
 
95
- That's it. Your data is automatically sandboxed, indexes are created, and cleanup is handled.
199
+ **That's it!** You now have a fully functional, production-ready todo API with automatic data sandboxing, index management, and lifecycle handling.
96
200
 
97
201
  ---
98
202
 
@@ -81,7 +81,7 @@ from .repositories import Entity, MongoRepository, Repository, UnitOfWork
81
81
  # Utilities
82
82
  from .utils import clean_mongo_doc, clean_mongo_docs
83
83
 
84
- __version__ = "0.2.4" # Patch version bump for exception handling improvements
84
+ __version__ = "0.3.1" # Patch version bump: Fix public route matching for mounted apps
85
85
 
86
86
  __all__ = [
87
87
  # Core Engine
@@ -82,6 +82,22 @@ def _compute_fingerprint(request: Request) -> str:
82
82
  return hashlib.sha256(fingerprint_string.encode()).hexdigest()
83
83
 
84
84
 
85
+ def _get_request_path(request: Request) -> str:
86
+ """
87
+ Get the request path relative to the mount point.
88
+
89
+ For mounted apps (via create_multi_app), use request.scope["path"] which
90
+ contains the path relative to the mount point. For non-mounted apps,
91
+ fall back to request.url.path.
92
+
93
+ This ensures public routes in manifests (which are relative paths like "/")
94
+ match correctly when apps are mounted at prefixes like "/auth-hub".
95
+ """
96
+ # Use scope["path"] which is relative to mount point for mounted apps
97
+ # Fall back to url.path for non-mounted apps
98
+ return request.scope.get("path", request.url.path)
99
+
100
+
85
101
  class SharedAuthMiddleware(BaseHTTPMiddleware):
86
102
  """
87
103
  Middleware for shared authentication across multi-app deployments.
@@ -169,7 +185,7 @@ class SharedAuthMiddleware(BaseHTTPMiddleware):
169
185
  # However, for Lazy middleware, we want to skip if not initialized yet
170
186
  return await call_next(request)
171
187
 
172
- is_public = self._is_public_route(request.url.path)
188
+ is_public = self._is_public_route(_get_request_path(request))
173
189
 
174
190
  # Extract token from cookie or header
175
191
  token = self._extract_token(request)
@@ -522,7 +538,7 @@ def _create_lazy_middleware_class(
522
538
  )
523
539
  return await call_next(request)
524
540
 
525
- is_public = _is_public_route_helper(request.url.path, self._public_routes)
541
+ is_public = _is_public_route_helper(_get_request_path(request), self._public_routes)
526
542
  token = _extract_token_helper(
527
543
  request, self._cookie_name, self._header_name, self._header_prefix
528
544
  )