AI-Powered Library System with Real-Time Data Sync
![]() |
![]() |
Shelf AI is an AI-powered library management system built as a Turborepo monorepo. It features two Next.js 16 dashboards (admin and user), a Rust-based SpacetimeDB server module, and shared UI/logic packages. AI capabilities, semantic search, book generation, and magic shuffle, are powered by Google Gemini via the Vercel AI SDK.
| User Dashboard | Admin Dashboard |
|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
|
![]() |
|
![]() |
|
![]() |
shelf-ai/
βββ apps/
β βββ admin/ β Admin dashboard (Next.js, port 3001)
β βββ user/ β User dashboard (Next.js, port 3002)
β βββ server/ β SpacetimeDB module (Rust/WASM)
βββ packages/
β βββ ui/ β Shared React component library
β βββ shared/ β Hooks, types, schemas, constants, i18n
β βββ eslint-config/ β Shared ESLint configuration
β βββ typescript-config/ β Shared TSConfig presets
βββ docs/tutorials/ β Setup and integration guides
βββ turbo.json β Turborepo pipeline config
βββ pnpm-workspace.yaml β Workspace definition
Traditional library apps use REST APIs with PostgreSQL. Shelf AI replaces this with SpacetimeDB, a serverless database where backend logic runs as WASN modules. Clients subscribe to table changes over WebSockets and receive real-time delta updates, eliminating polling and HTTP round-trips entirely.
sequenceDiagram
participant C as Next.js Client
participant WS as WebSocket Connection
participant W as WASM Reducer
participant T as In-Memory Tables
rect rgb(15, 42, 30)
Note over C: Action Dispatched
C->>WS: Call Reducer (e.g. addBook)
WS->>W: Execute WASM Logic
W->>T: Mutate In-Memory State
T-->>WS: Broadcast Delta to Subscribers
WS-->>C: onUpdate Callback β React Re-render
Note over C: UI Updated Instantly
end
sequenceDiagram
participant UC as Client (React)
participant RH as Next.js Route Handler
participant AI as Google Gemini 2.5 Flash
participant SDB as SpacetimeDB
UC->>RH: POST /api/ai/search { query, catalog }
RH->>AI: generateObject(prompt + zod schema)
AI-->>RH: Structured JSON (ranked results)
RH-->>UC: Return matches with relevance scores
opt Admin: Book Generation
UC->>RH: POST /api/ai/generate { prompt }
RH->>AI: generateObject(book metadata schema)
AI-->>RH: Complete book object
RH-->>UC: Return generated book
UC->>SDB: Persist via useAddBook reducer
end
opt Magic Shuffle
UC->>RH: POST /api/ai/shuffle { catalog }
RH->>AI: generateObject(themed picks schema)
AI-->>RH: Thematically linked books
RH-->>UC: Return picks + theme name
end
sequenceDiagram
participant U as User
participant C as Next.js App
participant CK as Clerk
participant SDB as SpacetimeDB
U->>C: Visit /sign-in
C->>CK: Render Clerk Sign-In Component
U->>CK: Enter Credentials
CK-->>C: Session Token + User Object
C->>SDB: Connect WebSocket (with clerkId)
SDB-->>C: Subscription Sync (user data, books, etc.)
C-->>U: Render Dashboard
graph LR
A([Home]) --> B([Search])
B --> C([Book Details])
C --> D{Available?}
D -->|Yes| E([Issue Book])
D -->|No| F([Reserve])
E --> G([Confirm + Student ID])
G --> H([Issued β])
style A fill:#10b981,color:#fff
style H fill:#10b981,color:#fff
style F fill:#f59e0b,color:#fff
graph LR
A([Home]) --> B([AI Search])
B --> C([Write Prompt])
C --> D([AI Ranked Suggestions])
D --> E([Book Details])
E --> F{Available?}
F -->|Yes| G([Issue])
F -->|No| H([Reserve])
G --> I([Confirm])
I --> J([Issued β])
style A fill:#10b981,color:#fff
style J fill:#10b981,color:#fff
style H fill:#f59e0b,color:#fff
graph LR
A([Home]) --> B([Search])
B --> C([Magic Shuffle π²])
C --> D([Themed Suggestions])
D --> E([Book Details])
E --> F{Available?}
F -->|Yes| G([Issue])
F -->|No| H([Reserve])
G --> I([Confirm])
I --> J([Issued β])
style A fill:#10b981,color:#fff
style J fill:#10b981,color:#fff
style C fill:#8b5cf6,color:#fff
style H fill:#f59e0b,color:#fff
graph LR
A([Home]) --> B([My Books])
B --> C([Select Book])
C --> D([Return])
D --> E([Rate Book β])
E --> F([Returned β])
style A fill:#10b981,color:#fff
style F fill:#10b981,color:#fff
style E fill:#f59e0b,color:#fff
graph LR
A([Admin Dashboard]) --> B([AI Generate])
B --> C([Enter Prompt])
C --> D([AI Generates Metadata])
D --> E([Review Book])
E --> F([Save to Library])
F --> G([Persisted in SpacetimeDB β])
style A fill:#6366f1,color:#fff
style G fill:#10b981,color:#fff
style D fill:#8b5cf6,color:#fff
erDiagram
LibraryUser {
string id PK
string clerk_id UK
string name
string email
UserRole role
string phone
string address
string avatar
string member_since
MembershipType membership_type
int borrow_limit
string bio
string register_number
string department
UserStatus status
timestamp created_at
}
Book {
string id PK
string title
string author
string isbn
BookCategory category
string published_date
string publisher
int total_copies
int available_copies
BookStatus status
string cover_url
string description
string location
string branch_id FK
BookFormat format
int pages
string language
string edition
float rating
timestamp created_at
timestamp updated_at
}
Branch {
string id PK
string name
string address
string city
string phone
string email
string manager
int total_books
int total_members
BranchStatus status
string open_hours
}
BorrowRecord {
string id PK
string book_id FK
string user_id FK
string borrow_date
string due_date
string return_date
BorrowStatus status
string branch_id FK
float fine
int renew_count
}
Notification {
string id PK
string user_id FK
NotificationType notification_type
string title
string message
string date
bool read
}
BookRating {
string id PK
string book_id FK
string user_id FK
float rating
string created_at
}
AiGeneration {
string id PK
string user_id FK
string prompt
string result_book_id FK
AiGenerationStatus status
timestamp created_at
}
Branch ||--o{ Book : houses
LibraryUser ||--o{ BorrowRecord : borrows
Book ||--o{ BorrowRecord : tracked_in
Branch ||--o{ BorrowRecord : processed_at
LibraryUser ||--o{ Notification : receives
LibraryUser ||--o{ BookRating : rates
Book ||--o{ BookRating : rated_by
LibraryUser ||--o{ AiGeneration : requests
Book ||--o{ AiGeneration : generated_as
| Feature | Endpoint | Description |
|---|---|---|
| Semantic Search | POST /api/ai/search |
Natural language queries ranked by relevance |
| Book Generation | POST /api/ai/generate |
Complete book metadata from a text prompt |
| Magic Shuffle | POST /api/ai/shuffle |
Thematically linked book discovery |
| Command | Description |
|---|---|
pnpm dev |
Start all dev servers via Turborepo |
pnpm build |
TypeScript check + production build (all apps) |
pnpm lint |
ESLint strict checks across monorepo |
pnpm check-types |
TypeScript verification for all packages |
pnpm format |
Prettier formatting |
- Node.js 22+
- pnpm 9+
- Rust toolchain (for SpacetimeDB server module)
- SpacetimeDB CLI (
spacetime)
git clone https://github.com/littlestmo/shelf-ai.git
cd turbo
pnpm installCopy and configure .env.local in both apps/user/ and apps/admin/:
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/home
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/home
NEXT_PUBLIC_SPACETIMEDB_URI=http://localhost:3000
NEXT_PUBLIC_SPACETIMEDB_MODULE=shelf-ai
GOOGLE_GENERATIVE_AI_API_KEY=your_key_herecd apps/server
spacetime start
# on a separate terminal:
spacetime publish -s local shelf-aipnpm devAdmin: http://localhost:3001 Β· User: http://localhost:3002
| Guide | Description |
|---|---|
| Environment Setup | Configure all environment variables |
| Clerk Auth Setup | Get Clerk API keys step by step |
| Google AI Setup | Get Gemini API key from Google AI Studio |
| SpacetimeDB Setup | Install CLI, publish module, connect client |
| AI Integration | How the Vercel AI SDK is used in the codebase |
| i18n Setup | Internationalization with react-i18next |
| Railway Deployment Guide | Deploy to Railway |
This work is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License.



















