Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions mflix/README-JAVASCRIPT-EXPRESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ This is a full-stack movie browsing application built with Express.js and Next.j

- **Node.js 22** or higher
- **MongoDB Atlas cluster or local deployment** with the `sample_mflix` dataset loaded
- [Load sample data](https://www.mongodb.com/docs/atlas/sample-data/)
- [Load sample data](https://www.mongodb.com/docs/atlas/sample-data/)
- **npm** (included with Node.js)
- **Voyage AI API key** (For MongoDB Vector Search)
- [Get a Voyage AI API key](https://www.voyageai.com/)
Expand Down Expand Up @@ -54,7 +54,6 @@ VOYAGE_API_KEY=your_voyage_api_key
PORT=3001
NODE_ENV=development


# CORS Configuration
# Allowed origin for cross-origin requests (frontend URL)
# For multiple origins, separate with commas
Expand Down
20 changes: 10 additions & 10 deletions mflix/README-PYTHON-FASTAPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ This is a full-stack movie browsing application built with Python FastAPI and Ne
- **Python 3.10** to **Python 3.13**
- **Node.js 20** or higher
- **MongoDB Atlas cluster or local deployment** with the `sample_mflix` dataset loaded
- [Load sample data](https://www.mongodb.com/docs/atlas/sample-data/)
- [Load sample data](https://www.mongodb.com/docs/atlas/sample-data/)
- **pip** for Python package management
- **Voyage AI API key** (For MongoDB Vector Search)
- [Get a Voyage AI API key](https://www.voyageai.com/)
Copy link
Copy Markdown
Collaborator

@shuangela shuangela Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another note: do we need them to install uvicorn from pip, or is that included in the requirements file/included if we add fastapi to requirements?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question! I didn't do anything special to install uvicorn, so I assume it came along with something that's already in requirements (maybe FastAPI). We can research that as a fast follow.

Expand Down Expand Up @@ -56,7 +56,7 @@ VOYAGE_API_KEY=your_voyage_api_key

# CORS Configuration
# Comma-separated list of allowed origins for CORS
CORS_ORIGINS=http://localhost:3000,http://localhost:8000
CORS_ORIGINS=http://localhost:3000,http://localhost:3001
```

**Note:** Replace `username`, `password`, and `cluster` with your actual MongoDB Atlas
Expand All @@ -69,7 +69,7 @@ python -m venv .venv
```

Activate the virtual environment:

```bash
source .venv/bin/activate
```
Expand All @@ -85,12 +85,13 @@ pip install -r requirements.txt
From the `server/` directory, run:

```bash
fastapi dev main.py --reload
uvicorn main:app --reload --port 3001
```

The server will start on `http://localhost:8000`. You can verify it's running by visiting:
- API root: http://localhost:8000/api/movies
- API documentation (Swagger UI): http://localhost:8000/docs
The server will start on `http://localhost:3001`. You can verify it's running by visiting:
- API root: http://localhost:3001/api/movies
- API documentation (Swagger UI): http://localhost:3001/docs
- Interactive API documentation (ReDoc): http://localhost:3001/redoc

### 3. Configure and Start the Frontend

Expand Down Expand Up @@ -118,8 +119,8 @@ The Next.js application will start on `http://localhost:3000`.

Open your browser and navigate to:
- **Frontend:** http://localhost:3000
- **Backend API:** http://localhost:8000
- **API Documentation:** http://localhost:8000/docs
- **Backend API:** http://localhost:3001
- **API Documentation:** http://localhost:3001/docs

## Features

Expand Down Expand Up @@ -204,4 +205,3 @@ If you have problems running the sample app, please check the following:
If you have verified the above and still have issues, please
[open an issue](https://github.com/mongodb/docs-sample-apps/issues/new/choose)
on the source repository `mongodb/docs-sample-apps`.

6 changes: 0 additions & 6 deletions mflix/client/.env.example

This file was deleted.

91 changes: 0 additions & 91 deletions mflix/client/README.md

This file was deleted.

2 changes: 1 addition & 1 deletion mflix/server/python-fastapi/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ VOYAGE_API_KEY=your_voyage_api_key

# CORS Configuration
# Comma-separated list of allowed origins for CORS
CORS_ORIGINS="http://localhost:3000,http://localhost:8000"
CORS_ORIGINS="http://localhost:3000,http://localhost:3001"
8 changes: 8 additions & 0 deletions mflix/server/python-fastapi/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ async def lifespan(app: FastAPI):
# Startup: Create search indexes
await ensure_search_index()
await vector_search_index()

# Print server information
print(f"\n{'='*60}")
print(f" Server started at http://127.0.0.1:3001")
print(f" Documentation at http://127.0.0.1:3001/docs")
print(f" Interactive API docs at http://127.0.0.1:3001/redoc")
print(f"{'='*60}\n")

yield
# Shutdown: Clean up resources if needed
# Add any cleanup code here
Expand Down
1 change: 0 additions & 1 deletion mflix/server/python-fastapi/src/database/mongo_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
client = AsyncMongoClient(os.getenv("MONGO_URI"))
db = client[os.getenv("MONGO_DB")]

# Set the API key but don't instantiate the client here
voyage_api_key = os.getenv("VOYAGE_API_KEY")
if voyage_api_key:
voyageai.api_key = voyage_api_key
Expand Down
11 changes: 0 additions & 11 deletions mflix/server/python-fastapi/src/models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,6 @@ class Movie(BaseModel):
"populate_by_name" : True
}


'''
So this an interesting conversion. Pydanic doesn't cleanly support constructing
models that have MongoDB query operators as field names. This becomes an issue when
we want to convert the validated model back to a dictionary to use as a MongoDB query filter.
For example, if a user leaves the 'q' parameter blank, we don't want to include the '$text' operator
in the filter at all but validation will send an empty value for it and that causes errors. So
I am handling the validation in the query router itself, but leaving this here as an example of how
it could be done, if I am wrong about Pydantic's capabilities.
'''

class TextFilter(BaseModel):
search: str = Field(..., alias="$search")

Expand Down
33 changes: 1 addition & 32 deletions mflix/server/python-fastapi/src/routers/movies.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ async def search_movies(
- plot: Movie plot text
- score: Vector search similarity score (0.0 to 1.0, higher = more similar)
"""
# Specify your Voyage API key and embedding model
# Specify your Voyage AI embedding model
model = "voyage-3-large"
outputDimension = 2048 #Set to 2048 to match the dimensions of the collection's embeddings

Expand Down Expand Up @@ -403,10 +403,6 @@ async def vector_search_movies(
details=str(e)
)

#------------------------------------
# Place get_movie_by_id endpoint here
#------------------------------------

"""
GET /api/movies/{id}
Retrieve a single movie by its ID.
Expand Down Expand Up @@ -539,10 +535,6 @@ async def get_all_movies(
# Return the results wrapped in a SuccessResponse
return create_success_response(movies, f"Found {len(movies)} movies.")

#------------------------------------
# Place create_movie endpoint here
#------------------------------------

"""
POST /api/movies/
Create a new movie.
Expand Down Expand Up @@ -600,10 +592,6 @@ async def create_movie(movie: CreateMovieRequest):

return create_success_response(created_movie, f"Movie '{movie_data['title']}' created successfully")

#------------------------------------
# Place create_movies_batch endpoint here
#------------------------------------

"""
POST /api/movies/batch

Expand Down Expand Up @@ -670,10 +658,6 @@ async def create_movies_batch(movies: List[CreateMovieRequest]) ->SuccessRespons
details=str(e)
)

#------------------------------------
# Place update_movie endpoint here
#------------------------------------

"""
PATCH /api/movies/{id}

Expand Down Expand Up @@ -744,10 +728,6 @@ async def update_movie(

return create_success_response(updatedMovie, f"Movie updated successfully. Modified {len(update_dict)} fields.")

#------------------------------------
# Place update_movies_by_batch endpoint here
#------------------------------------

"""
PATCH /api/movies

Expand Down Expand Up @@ -810,10 +790,6 @@ async def update_movies_batch(
f"Update operation completed. Matched {result.matched_count} movie(s), modified {result.modified_count} movie(s)."
)

#------------------------------------
# Place delete_movie endpoint here
#------------------------------------

"""
DELETE /api/movies/{id}
Delete a single movie by its ID.
Expand Down Expand Up @@ -860,9 +836,6 @@ async def delete_movie_by_id(id: str):
"Movie deleted successfully"
)

#------------------------------------
# Place delete_movies_by_batch endpoint here
#------------------------------------
"""
DELETE /api/movies/

Expand Down Expand Up @@ -922,10 +895,6 @@ async def delete_movies_batch(request_body: dict = Body(...)) -> SuccessResponse
f'Delete operation completed. Removed {result.deleted_count} movies.'
)

#------------------------------------
# Place find_and_delete_movie endpoint here
#------------------------------------

"""
DELETE /api/movies/{id}/find-and-delete
Finds and deletes a movie in a single atomic operation.
Expand Down
24 changes: 2 additions & 22 deletions mflix/server/python-fastapi/src/utils/errorHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,9 @@
from fastapi.responses import JSONResponse
from pymongo.errors import PyMongoError, DuplicateKeyError, WriteError
from datetime import datetime, timezone
from typing import Any, Dict, Optional
from typing import Any, Optional
from src.models.models import ErrorDetails, ErrorResponse, SuccessResponse, T


'''
Open to having a conversation about parity in the code. From my understanding exception handeling, validation errors and enforcement(Pydantic),
and error response formatting are all handled natively by FastAPI. So I don't believe I need to create the ValidationError
class, middleware, or exception handlers present in the TS code.

'''


'''
Creates a standardized success response.

Expand All @@ -26,8 +17,6 @@
SuccessResponse[T]: A standardized success response object.
'''


# TODO: Verify the timestamp format is acceptable.
def create_success_response(data:T, message: Optional[str] = None) -> SuccessResponse[T]:
return SuccessResponse(
message=message or "Operation completed successfully.",
Expand All @@ -36,7 +25,6 @@ def create_success_response(data:T, message: Optional[str] = None) -> SuccessRes

)


'''
Creates a standardized error response.

Expand All @@ -50,7 +38,6 @@ def create_success_response(data:T, message: Optional[str] = None) -> SuccessRes

'''

# TODO: Verify the timestamp format is acceptable.
def create_error_response(message: str, code: Optional[str]=None, details: Optional[Any]=None) -> ErrorResponse:
return ErrorResponse(
message=message,
Expand All @@ -63,13 +50,6 @@ def create_error_response(message: str, code: Optional[str]=None, details: Optio
)



'''
This is interesting, I am not sure if this is worth explaining that compared to Node, you are
not going to get exceptions thrown from MongoDB operations in the same way. You are not getting
error codes back from operations, you are getting exceptions.
'''

def parse_mongo_exception(exc: Exception) -> dict:
if isinstance(exc, DuplicateKeyError):
return{
Expand Down Expand Up @@ -126,4 +106,4 @@ async def generic_exception_handler(request: Request, exc: Exception):
code="INTERNAL_SERVER_ERROR",
details=getattr(exc, 'detail', None) or getattr(exc, 'args', None)
).model_dump()
)
)
2 changes: 1 addition & 1 deletion mflix/server/python-fastapi/tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ The test suite is organized into three categories:

1. **For all tests:**
```bash
cd server/python-fastapi
cd server/
source .venv/bin/activate # or `.venv\Scripts\activate` on Windows
```

Expand Down
Loading