A library management system built with ASP.NET Core Blazor Server and MongoDB. Leafy Library demonstrates intelligent data application patterns using MongoDB Atlas Search, vector embeddings, and advanced aggregation pipelines.
- Browse and search a book catalogue with full-text Atlas Search
- Borrow and return books with inventory management
- Reserve books with automatic 12-hour TTL expiration
- Write and view reviews with star ratings
- User dashboard with loan statistics and reading history
- Admin panel for managing books, loans, and reservations
- Semantic search via vector embeddings (OpenAI / Azure OpenAI / VoyageAI)
- JWT authentication with automatic admin promotion for the first user
| Requirement | Version |
|---|---|
| .NET SDK | 10.0+ |
| MongoDB | 7.0+ (local) or Atlas |
| Git | Any recent version |
For vector/semantic search (optional): An API key for one of the following embedding providers:
git clone <repo-url>
cd "Leafy Library"Copy or edit appsettings.json with your settings. At a minimum, update the MongoDB connection string and JWT secret:
{
"MongoDb": {
"ConnectionString": "mongodb://localhost:27017",
"DatabaseName": "leafy_library"
},
"Jwt": {
"Secret": "your-secure-secret-key-at-least-32-characters-long",
"ExpiryInDays": 365,
"Issuer": "LeafyLibrary",
"Audience": "LeafyLibraryUsers"
}
}For an Atlas cluster, replace the connection string with your Atlas URI:
mongodb+srv://<username>:<password>@<cluster>.mongodb.net/?retryWrites=true&w=majority
Important: Never commit your real JWT secret or Atlas credentials. Use
appsettings.Development.jsonor environment variables for local secrets.
dotnet run --project "Leafy Library"The app starts at https://localhost:5001 by default. The first time you log in, your account is automatically promoted to admin.
The app starts with an empty database. You can populate it by importing data via the Admin panel once logged in, or by loading a MongoDB dump directly into the leafy_library database.
Leafy Library/
├── Controllers/ # REST API endpoints
│ ├── BooksController
│ ├── AuthorsController
│ ├── ReviewsController
│ ├── IssueDetailsController
│ └── ReservationsController
├── Services/ # Business logic
│ ├── DatabaseService # MongoDB connection and typed collections
│ ├── BookService # Book CRUD and Atlas Search
│ ├── AuthorService # Author queries and relationships
│ ├── ReviewService # Reviews with subset pattern sync
│ ├── UserService # User creation and lookup
│ ├── TokenService # JWT generation and validation
│ ├── IssueDetailService # Loan and reservation logic + aggregations
│ ├── ReservationService # Book reservation management
│ └── EmbeddingService # Vector embedding generation
├── Models/ # BSON-mapped data models
│ ├── Book
│ ├── Author
│ ├── Review
│ ├── User
│ ├── IssueDetail
│ └── (DTOs and settings models)
├── Components/ # Blazor UI
│ ├── Pages/
│ │ ├── Home.razor # Book catalogue with search
│ │ ├── BookDetail.razor # Book info, reviews, borrow/reserve
│ │ ├── AuthorDetail.razor # Author bio and books
│ │ ├── Login.razor # Login and auto-registration
│ │ ├── Dashboard.razor # Loan stats and reading history
│ │ └── Admin.razor # Admin management panel
│ ├── Layout/ # App shell and navigation
│ └── Shared/ # Reusable components (StarRating, ReviewForm, etc.)
├── Program.cs # Dependency injection and middleware setup
├── appsettings.json # Application configuration
└── Leafy Library.csproj # Project file
| Layer | Technology |
|---|---|
| Frontend | Blazor Server (interactive SSR) |
| Backend | ASP.NET Core 10 REST API |
| Database | MongoDB |
| Auth | JWT (stored in ProtectedBrowserStorage) |
| Search | MongoDB Atlas Search ($search) |
| Semantic Search | Vector embeddings via $vectorSearch |
| Collection | Description |
|---|---|
books |
Book catalogue with embedded reviews (subset pattern) and optional embedding vector |
authors |
Author profiles with references to books |
reviews |
Full review documents (also partially embedded in books) |
users |
User accounts with admin flag |
issueDetails |
Unified collection for loans and reservations (single-collection pattern) |
Extended Reference Pattern — Book documents embed author names alongside the author ID to avoid lookups for display.
Subset Pattern — Each book stores the 5 most recent reviews inline. The full review history lives in the reviews collection.
Single-Collection Pattern — Loans and reservations share the issueDetails collection, distinguished by a type field and composite ID format.
TTL Index — Reservations automatically expire after 12 hours via a MongoDB TTL index on expirationDate.
Atomic Inventory — $inc operations handle concurrent borrow and return operations safely.
Full-text search is powered by a MongoDB Search index named fulltextsearch. The app creates this index automatically on startup if it does not exist. It covers:
- Book title
- Author names
- Genres
Vector search uses the embedding field on book documents. Embeddings are generated from the book synopsis and stored at indexing time.
- User submits a username on the Login page.
- A
GET /api/users/login/{username}request gets or creates the user. - A signed JWT is returned and stored in browser protected storage.
- Subsequent requests include the JWT as a Bearer token.
- The first user account created is automatically promoted to admin.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/books |
— | List books (paginated) |
| GET | /api/books/{id} |
— | Get book by ID |
| GET | /api/books/search?q= |
— | Full-text search |
| POST | /api/books |
Required | Create book |
| PUT | /api/books/{id} |
Required | Update book |
| DELETE | /api/books/{id} |
Required | Delete book |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/authors |
— | List authors (paginated) |
| GET | /api/authors/{id} |
— | Get author |
| GET | /api/authors/name/{name} |
— | Get by sanitized name |
| GET | /api/authors/search?q= |
— | Search authors |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/books/{bookId}/reviews |
— | List reviews for a book |
| POST | /api/books/{bookId}/reviews |
Required | Add a review |
| DELETE | /api/books/{bookId}/reviews/{id} |
Required | Delete a review |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/issuedetails/user/{userId}/active |
Required | Active borrows |
| POST | /api/issuedetails/borrow |
Required | Borrow a book |
| POST | /api/issuedetails/return/{issueId} |
Required | Return a book |
| POST | /api/issuedetails/borrow/{bookId}/{userId} |
Admin | Admin lend a book |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/reservations |
Required | List user's reservations |
| POST | /api/reservations/{bookId} |
Required | Reserve a book |
| DELETE | /api/reservations/{bookId} |
Required | Cancel a reservation |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/users/login/{username} |
Login or register, returns JWT |
This repo is structured around an incremental tutorial series:
| Branch | Description |
|---|---|
main |
Production-ready baseline |
start |
Starting point — minimal implementation |
with-text-search |
Search full-text search added |
with-vector-search |
Vector embeddings and semantic search added |
| Key | Default | Description |
|---|---|---|
MongoDb:ConnectionString |
mongodb://localhost:27017 |
MongoDB connection URI |
MongoDb:DatabaseName |
leafy_library |
Database name |
Jwt:Secret |
(must change) | Signing key, minimum 32 characters |
Jwt:ExpiryInDays |
365 |
Token lifetime in days |
Jwt:Issuer |
LeafyLibrary |
JWT issuer claim |
Jwt:Audience |
LeafyLibraryUsers |
JWT audience claim |
Embedding:Provider |
(optional) | OpenAI, AzureOpenAI, or VoyageAI |
Embedding:ApiKey |
(optional) | API key for the embedding provider |
Embedding:Model |
(optional) | Embedding model name |
Embedding:Dimensions |
1536 |
Vector dimensions |
Embedding:Endpoint |
(optional) | Azure OpenAI endpoint URL |