Version: 0.0.0 | Stack: React 19 + Express + Prisma + Groq AI
- Overview
- Architecture
- Tech Stack
- Database Schema
- API Reference
- Authentication & Security
- AI Integration
- Frontend Architecture
- Environment Variables
- Rate Limiting
- Development Setup
- Build & Deployment
- Project Structure
StudySync AI is a full-stack, enterprise-ready AI-powered productivity and learning platform. It integrates a smart study planner, AI-guided tutoring, assignment tracking, flashcard management, quiz generation, and learning analytics into one seamless workspace.
Key capabilities:
- AI Tutor with conversational chat (Groq Llama 3.3 70B)
- AI-powered quiz generator with configurable difficulty
- AI-generated flashcards from any topic
- AI-driven study suggestions
- Assignment CRUD with priority and status tracking
- Subject management with progress tracking
- Rich note-taking with markdown support (starred, searchable)
- Flashcard deck library with flip-card study mode
- Interactive study planner with weekly calendar view
- Analytics dashboard with charts and achievement badges
- Dark-theme UI with responsive mobile layout
Browser
│
├── [SPA] React 19 + Vite (port 5173 dev / served by Express in prod)
│ │
│ ├── AuthContext (JWT in localStorage)
│ ├── TanStack React Query (server state)
│ └── Axios client (interceptors for auth header + 401 redirect)
│
└── [API] Express 4 (port 3000)
│
├── Helmet (security headers)
├── CORS (restricted origins)
├── Morgan (HTTP logging)
├── Rate Limiters (multi-layer)
├── JWT Auth Middleware (/api/*)
│
├── /auth/* ──► bcrypt + JWT ──► PostgreSQL (Prisma)
├── /api/* ──► CRUD routes ──► PostgreSQL (Prisma)
└── /api/ai/* ──► Groq SDK ──► Llama 3.3 70B
- Monolithic but modular: Single Express server serves both the API and the SPA.
- Dev mode: Vite dev server middleware for HMR.
- Production mode: Serves compiled static assets from
dist/. - SPA fallback: All unrecognized routes serve
index.html.
| Layer | Technology |
|---|---|
| Framework | React 19, TypeScript |
| Build | Vite 6, esbuild |
| Routing | React Router 7 |
| Styling | Tailwind CSS 4, clsx, tailwind-merge |
| Charts | Recharts 3 |
| Animations | Motion 12 (Framer Motion successor) |
| Data Fetching | TanStack React Query 5 |
| HTTP Client | Axios |
| Markdown | react-markdown 10 |
| Icons | Lucide React |
| Notifications | react-hot-toast 2 |
| Dates | date-fns 4 |
| Layer | Technology |
|---|---|
| Runtime | Node.js 22, TypeScript, ESM |
| Framework | Express 4 |
| Database ORM | Prisma 5 + PostgreSQL |
| Auth | jsonwebtoken 9 (HS256), bcrypt 6 |
| AI | Groq SDK 1.3 (Llama 3.3 70B) |
| Security | Helmet 8, express-rate-limit 8 |
| Logging | Morgan |
| Dev Runner | tsx (TypeScript execution) |
User ──┬── Subject ──┬── Assignment
│ └── Note
├── Assignment (direct FK)
├── Note (direct FK)
└── Deck ──┬── Flashcard (cascade delete)
| Field | Type | Constraints |
|---|---|---|
| id | UUID |
PK, default gen_random_uuid() |
| name | VARCHAR(255) |
NOT NULL |
VARCHAR(255) |
UNIQUE, NOT NULL | |
| password | VARCHAR(255) |
NOT NULL (bcrypt hash) |
| createdAt | TIMESTAMP |
default now() |
| updatedAt | TIMESTAMP |
auto-updated |
| Field | Type | Constraints |
|---|---|---|
| id | UUID |
PK |
| name | VARCHAR(255) |
NOT NULL |
| code | VARCHAR(100) |
NOT NULL |
| instructor | VARCHAR(255) |
optional |
| progress | INT |
default 0, min 0, max 100 |
| userId | UUID |
FK → User, NOT NULL |
| Field | Type | Constraints |
|---|---|---|
| id | UUID |
PK |
| title | VARCHAR(500) |
NOT NULL |
| due | TIMESTAMP |
NOT NULL |
| priority | ENUM(High, Medium, Low) |
default Medium |
| status | ENUM(pending, completed) |
default pending |
| subjectId | UUID |
FK → Subject |
| userId | UUID |
FK → User, NOT NULL |
| Field | Type | Constraints |
|---|---|---|
| id | UUID |
PK |
| title | VARCHAR(500) |
NOT NULL |
| preview | VARCHAR(500) |
NOT NULL |
| content | TEXT |
optional |
| starred | BOOLEAN |
default false |
| subjectId | UUID |
FK → Subject |
| userId | UUID |
FK → User, NOT NULL |
| Field | Type | Constraints |
|---|---|---|
| id | UUID |
PK |
| name | VARCHAR(500) |
NOT NULL |
| lastReviewed | TIMESTAMP |
optional |
| mastery | INT |
default 0, min 0, max 100 |
| userId | UUID |
FK → User, NOT NULL |
| Field | Type | Constraints |
|---|---|---|
| id | UUID |
PK |
| front | TEXT |
NOT NULL |
| back | TEXT |
NOT NULL |
| deckId | UUID |
FK → Deck, NOT NULL, cascade delete |
All auth endpoints are behind authLimiter (20 req/15min per IP+email), authStrictLimiter (5 req/hr), and exponential authBackoffMiddleware.
Base path: /auth
Create a new account.
| Field | Type | Validation |
|---|---|---|
| name | string | 1–100 chars, alphabetic + spaces/hyphens/apostrophes |
| string | Valid format, max 254 chars | |
| password | string | 8–128 chars |
Success (201):
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"user": { "id": "uuid", "name": "Alice", "email": "alice@example.com" }
}Errors: 400 (validation), 409 (email exists), 429 (rate limited)
Authenticate with existing credentials.
| Field | Type |
|---|---|
| string | |
| password | string |
Success (200): Same shape as register. Errors: 400 (validation), 401 (invalid credentials), 429 (rate limited)
All routes require Authorization: Bearer <token> header.
Rate limit: writeLimiter (30 req/min per user) on all mutations.
| Method | Path | Action |
|---|---|---|
| GET | /api/subjects |
List all subjects for the authenticated user |
| POST | /api/subjects |
Create a subject |
| PUT | /api/subjects/:id |
Update a subject (name, code, instructor, progress) |
| DELETE | /api/subjects/:id |
Delete a subject |
POST/PUT body:
{
"name": "Data Structures",
"code": "CS201",
"instructor": "Dr. Smith",
"progress": 50
}| Method | Path | Action |
|---|---|---|
| GET | /api/assignments |
List all assignments (includes subject relation) |
| POST | /api/assignments |
Create an assignment |
| PUT | /api/assignments/:id |
Update an assignment |
| DELETE | /api/assignments/:id |
Delete an assignment |
POST/PUT body:
{
"title": "Homework 3",
"due": "2025-12-01T23:59:00Z",
"priority": "High",
"status": "pending",
"subjectId": "uuid"
}| Method | Path | Action |
|---|---|---|
| GET | /api/notes |
List all notes (includes subject relation) |
| POST | /api/notes |
Create a note |
| PUT | /api/notes/:id |
Update a note (title, preview, content, starred) |
| DELETE | /api/notes/:id |
Delete a note |
POST/PUT body:
{
"title": "Binary Search Trees",
"preview": "Key operations and traversal methods...",
"content": "# BST\n\n## Operations\n...",
"subjectId": "uuid",
"starred": true
}| Method | Path | Action |
|---|---|---|
| GET | /api/decks |
List all decks (includes embedded flashcards) |
| POST | /api/decks |
Create a deck with optional cards |
| DELETE | /api/decks/:id |
Delete a deck (cascades to cards) |
POST body:
{
"name": "CS Fundamentals",
"cards": [
{ "front": "What is Big O?", "back": "Worst-case complexity measure" },
{ "front": "What is a stack?", "back": "LIFO data structure" }
]
}Rate limit: aiLimiter (10 req/min per user).
All AI endpoints are at /api/ai/ and use Groq's llama-3.3-70b-versatile model.
{
"message": "Explain how Dijkstra's algorithm works",
"history": [
{ "role": "user", "content": "What is a graph?" },
{ "role": "assistant", "content": "A graph is..." }
]
}message: max 5000 chars, requiredhistory: last 5 messages max, each content max 2000 chars- Returns:
{ "response": "Dijkstra's algorithm..." }(markdown formatted)
- No input required.
- Returns a 1-sentence AI-generated suggestion for a CS student.
- Graceful fallback if
GROQ_API_KEYis missing.
{ "topic": "Binary Search Trees" }topic: max 500 chars- Returns 5 flashcards as a JSON array:
[
{ "front": "What is a BST?", "back": "A tree where left < root < right" }
]{
"topic": "Database Normalization",
"difficulty": "Advanced",
"questions": 10
}topic: max 500 chars, requireddifficulty:Beginner|Intermediate(default) |Advancedquestions: 1–20 (default 10)- Returns:
[
{
"question": "What is 3NF?",
"options": ["...", "...", "...", "..."],
"correctAnswer": 2
}
]- User registers or logs in via
/auth/*. - Server validates credentials, returns JWT (HS256, 7-day expiry) with payload
{ id: userId }. - Client stores token + user in
localStorage. - Axios interceptor attaches
Authorization: Bearer <token>to all/api/*requests. - On 401 response, Axios interceptor clears localStorage and redirects to
/login. AuthContextchecks localStorage on mount to restore session.
| Measure | Implementation |
|---|---|
| Password storage | bcrypt with configurable cost (default 10) |
| JWT algorithm | HS256 with algorithms: ['HS256'] verification |
| Input validation | Type, length, regex patterns for name/email/UUID on server |
| User enumeration | Generic "Invalid credentials" on login failure |
| Rate limiting | 4 layers: general, auth, strict auth, exponential backoff |
| Security headers | Helmet with custom CSP |
| CORS | Restricted to configured origins |
| Error safety | Stack traces suppressed in production |
| Request size | 100kb JSON body limit |
Groq Cloud with model llama-3.3-70b-versatile at temperature 0.7 (0.9 for suggestions).
All four AI endpoints (chat, suggestion, flashcards, quiz) are defined inline in server.ts.
- Chat history: Last 5 user/assistant messages sent as context.
- Flashcard parsing: AI returns JSON wrapped in markdown code fences; parser strips fences and retries on parse failure.
- Quiz parsing: AI returns JSON array directly; current implementation requires valid JSON output.
- Graceful degradation: If
GROQ_API_KEYenv var is missing, the suggestion endpoint returns a static fallback message. - System prompt: "You are StudySync AI, an expert AI productivity assistant and learning tutor..."
| Route | Page | Auth Required |
|---|---|---|
/ |
Landing | No |
/login |
Login | No |
/register |
Register | No |
/forgot-password |
ForgotPassword | No |
/dashboard |
Dashboard | Yes |
/dashboard/ai-tutor |
AI Tutor | Yes |
/dashboard/quiz |
Quiz Generator | Yes |
/dashboard/planner |
Study Planner | Yes |
/dashboard/assignments |
Assignments | Yes |
/dashboard/analytics |
Analytics | Yes |
/dashboard/subjects |
Subjects | Yes |
/dashboard/flashcards |
Flashcards | Yes |
/dashboard/notes |
Notes | Yes |
/dashboard/settings |
Settings | Yes |
/dashboard/calendar |
Coming Soon | Yes |
/dashboard/homework |
Coming Soon | Yes |
/dashboard/exams |
Coming Soon | Yes |
/dashboard/notifications |
Coming Soon | Yes |
All dashboard pages are lazy-loaded with React.lazy + Suspense (spinner fallback) + ErrorBoundary.
| State Type | Mechanism |
|---|---|
| Auth | React Context + localStorage |
| Server data | TanStack React Query (auto cache invalidation) |
| UI state (modals, tabs, forms) | Local useState |
| Profile/Notifications | localStorage (no backend endpoint yet) |
| Component | Role |
|---|---|
Layout |
App shell: Sidebar + Header + <Outlet> |
Sidebar |
15 nav items, mobile overlay, active highlighting |
Header |
Search bar, notification bell, user avatar with gradient initials |
ErrorBoundary |
Class-based catch with refresh/dashboard recovery |
- Dark theme:
#0A0C10(bg),#11141D(cards),#1F2937(borders) - Accent: blue-500 primary, purple/emerald/rose/amber for variety
- Utility:
cn()fromsrc/lib/utils.ts(clsx + tailwind-merge) - Accessibility: skip-to-content, aria labels, focus-visible, reduced-motion support
| Variable | Description |
|---|---|
GROQ_API_KEY |
Groq Cloud API key |
DATABASE_URL |
PostgreSQL connection string |
JWT_SECRET |
JWT signing secret (min 32 characters) |
| Variable | Default | Description |
|---|---|---|
APP_URL |
http://localhost:3000 |
Application base URL |
PORT |
3000 |
Server port |
ALLOWED_ORIGINS |
http://localhost:3000 |
Comma-separated CORS origins |
BCRYPT_ROUNDS |
10 |
bcrypt salt rounds (min 10) |
JWT_EXPIRES_IN |
7d |
JWT expiry duration |
DISABLE_HMR |
(unset) | Set true to disable Vite HMR (AI Studio) |
| Variable | Default | Description |
|---|---|---|
GENERAL_LIMITER_WINDOW_MS |
900000 (15 min) | General rate limit window |
GENERAL_LIMITER_MAX |
100 | General rate limit max requests |
AUTH_LIMITER_WINDOW_MS |
900000 (15 min) | Auth rate limit window |
AUTH_LIMITER_MAX |
20 | Auth rate limit max requests |
AUTH_STRICT_LIMITER_WINDOW_MS |
3600000 (1 hr) | Strict auth limit window |
AUTH_STRICT_LIMITER_MAX |
5 | Strict auth limit max requests |
AI_LIMITER_WINDOW_MS |
60000 (1 min) | AI rate limit window |
AI_LIMITER_MAX |
10 | AI rate limit max requests |
WRITE_LIMITER_WINDOW_MS |
60000 (1 min) | Write rate limit window |
WRITE_LIMITER_MAX |
30 | Write rate limit max requests |
┌──────────────────────┐
│ generalLimiter │
│ 100 req / 15 min (IP)│
└────────┬─────────────┘
│
┌────────────┴────────────┐
│ │
┌────────┴────────┐ ┌────────┴────────┐
│ /auth/* │ │ /api/* │
│ │ │ /api/ai/* │
└────────┬─────────┘ └────────┬────────┘
│ │
┌────────┴────────┐ ┌────────┴────────┐
│ authLimiter │ │ aiLimiter │
│ 20/15min │ │ 10 req / 1 min │
│ (IP + email) │ │ (user ID) │
└────────┬────────┘ └─────────────────┘
│
┌────────┴────────┐
│ authStrictLim │
│ 5 / 1 hr (IP) │
└────────┬────────┘
│
┌────────┴────────┐
│ authBackoffMid │
│ exponential: │
│ 30s→2m→8m→30m→2h│
└─────────────────┘
Backoff entries are stored in-memory and auto-cleaned every 10 minutes. clearAuthFailureMiddleware resets backoff on successful login.
- Node.js 22+
- PostgreSQL (local or remote)
- Groq API key (free at console.groq.com)
# 1. Clone and enter the project
cd studysync-ai
# 2. Install dependencies
npm install
# 3. Copy environment file and fill in values
cp .env.example .env
# Edit .env: set GROQ_API_KEY, DATABASE_URL, JWT_SECRET
# 4. Run database migrations
npx prisma migrate dev --name init
# 5. Start dev server (runs both frontend + backend)
npm run devThe app is now available at http://localhost:3000.
| Command | Description |
|---|---|
npm run dev |
Start development server (Vite HMR + Express) |
npm run build |
Build frontend + bundle backend |
npm run start |
Start production server |
npm run lint |
TypeScript type-check (tsc --noEmit) |
npm run preview |
Vite preview of built frontend |
npx prisma studio |
Open Prisma Studio (DB GUI) |
npx prisma migrate dev |
Apply schema changes to database |
npm run buildThis runs:
vite build— compiles React frontend todist/esbuild server.ts— bundles Express backend todist/server.cjs
NODE_ENV=production npm run start- Set
NODE_ENV=production - Configure all env vars (especially
JWT_SECRETwith a strong 64+ char value) - Ensure PostgreSQL is accessible from the deployment environment
- Run
npx prisma migrate deployto apply migrations - Set
ALLOWED_ORIGINSto the production domain (or omit for same-origin) - Configure reverse proxy (nginx/Caddy) for SSL termination if needed
- Adjust rate limits if deploying behind a proxy (use
trust proxy)
To containerize:
- Build the app:
npm run build - Create a
Dockerfilewith Node.js 22 base - Copy
dist/,package.json,node_modules/,prisma/ - Run:
node dist/server.cjs
studysync-ai/
├── .env.example # Environment variable template
├── .gitignore
├── index.html # Vite SPA entry point
├── metadata.json # AI Studio manifest
├── package.json
├── tsconfig.json
├── vite.config.ts
├── server.ts # Express server entry (routes + AI endpoints)
├── README.md
├── DOCUMENTATION.md # This file
├── prisma/
│ └── schema.prisma # Database schema (6 models)
├── assets/ # Static assets
├── dist/ # Build output (gitignored)
├── node_modules/ # Dependencies (gitignored)
└── src/
├── main.tsx # React entry point
├── App.tsx # Router + layout
├── index.css # Tailwind + global styles
├── context/
│ └── AuthContext.tsx # Auth state management
├── lib/
│ ├── api.ts # Axios instance with interceptors
│ └── utils.ts # cn() utility (clsx + tailwind-merge)
├── components/
│ ├── Layout.tsx # App shell (Sidebar + Header + Outlet)
│ ├── Sidebar.tsx # Navigation sidebar
│ ├── Header.tsx # Top header bar
│ └── ErrorBoundary.tsx # Error boundary wrapper
├── pages/
│ ├── Landing.tsx # Marketing / landing page
│ ├── Login.tsx # Login form
│ ├── Register.tsx # Registration form
│ ├── ForgotPassword.tsx# Password reset (simulated)
│ ├── Dashboard.tsx # Main dashboard with stats + AI suggestion
│ ├── AITutor.tsx # AI chat interface
│ ├── Quiz.tsx # Quiz generator + taker
│ ├── Planner.tsx # Weekly study planner
│ ├── Assignments.tsx # Assignment CRUD
│ ├── Analytics.tsx # Charts + stats
│ ├── Subjects.tsx # Subject management
│ ├── Flashcards.tsx # Deck library + study mode
│ ├── Notes.tsx # Note-taking CRUD
│ ├── Settings.tsx # User settings (profile, appearance, security)
│ └── NotFound.tsx # 404 page
└── server/
├── config.ts # JWT config from env
├── auth.ts # JWT authentication middleware
├── db.ts # Prisma client singleton
├── rateLimit.ts # Multi-layer rate limiting
└── routes/
├── auth.ts # Auth routes (register, login)
└── api.ts # Protected CRUD routes