An AI-powered assessment and question paper generator for teachers. Create professional, exam-ready question papers in minutes.
Important
Current Limitation: PDF and image uploading for context is currently disabled. Free AI models on OpenRouter do not yet support direct file/image analysis. AI generation relies on the user's "Additional Information" field only.
Assignmento uses an asynchronous, worker-based architecture to handle AI generation without blocking the UI.
graph TD
A[Frontend: Next.js 16] -- POST /api/assignments --> B[Backend: Express 5]
B -- Zod Validation --> B
B -- Create Doc --> C[MongoDB Atlas]
B -- Queue Job --> D[Upstash Redis + BullMQ]
D -- Process --> E[AI Worker]
E -- Call AI --> F[OpenRouter]
F -- AI_MODELS fallback chain --> E
E -- Update Doc --> C
E -- Emit Event --> G[Socket.io]
G -- Notify Client --> A
| Layer | Technology |
|---|---|
| Frontend | Next.js 16 (App Router), TypeScript, TailwindCSS 4 |
| State | Zustand, React Context |
| Backend | Node.js, Express 5 |
| Database | MongoDB Atlas (Mongoose) |
| Queue | BullMQ + Upstash Redis |
| Real-time | Socket.io |
| AI | OpenRouter — hardcoded AI_MODELS fallback chain |
| html2canvas + jsPDF | |
| Icons | Lucide React |
Assignmento/
├── backend/
│ ├── server.js # Entry point — HTTP server, Socket.io, worker init
│ ├── app.js # Express app — middleware, routes
│ ├── .env # Local environment variables
│ ├── .env.example # Environment variables template
│ ├── config/
│ │ ├── db.js # MongoDB connection
│ │ ├── openrouter.js # OpenRouter AI client
│ │ └── redis.js # Redis/ioredis client
│ ├── models/
│ │ ├── assignmentModel.js # Assignment schema with nested questions
│ │ ├── sectionSchema.js # Nested schema for paper sections
│ │ ├── questionSchema.js # Nested schema for questions
│ │ └── questionConfigItemSchema.js # Question type configuration schema
│ ├── controllers/
│ │ └── assignmentController.js # HTTP handlers for assignment endpoints
│ ├── routes/
│ │ └── assignmentRouter.js # CRUD endpoints for assignments
│ ├── middlewares/
│ │ └── errorHandler.js # Global error handler
│ ├── services/
│ │ ├── assignmentService.js # Business logic + Redis caching
│ │ └── aiService.js # AI prompt execution + JSON parsing
│ ├── prompts/
│ │ └── assessmentPrompt.js # Builds AI system prompt from question config
│ ├── queues/
│ │ └── aiQueue.js # BullMQ queue setup
│ ├── workers/
│ │ └── aiWorker.js # Background AI job processor
│ └── socket/
│ └── socketHandler.js # Socket.io room subscriptions
│
└── frontend/
├── app/
│ ├── layout.tsx # Root layout (ToastProvider)
│ ├── page.tsx # Redirects to /assignments
│ ├── globals.css # Global styles + CSS variables (light/dark)
│ └── assignments/
│ ├── page.tsx # Assignment list/dashboard
│ ├── create/page.tsx # Create assignment form
│ └── [id]/page.tsx # Assignment detail + PDF preview
├── components/
│ ├── layout/
│ │ ├── AppShell.tsx # Main layout wrapper
│ │ ├── Header.tsx # Top bar — page label, dark mode toggle
│ │ ├── Sidebar.tsx # Desktop sidebar navigation
│ │ ├── MobileHeader.tsx # Mobile top bar
│ │ └── BottomNav.tsx # Mobile bottom navigation
│ ├── assignments/
│ │ ├── AssignmentCard.tsx # Individual assignment card
│ │ ├── AssignmentGrid.tsx # Grid + infinite scroll
│ │ ├── AssignmentsShimmer.tsx # Skeleton loading state
│ │ ├── SearchBar.tsx # Search input
│ │ └── EmptyState.tsx # Empty list illustration
│ ├── create/
│ │ ├── CreateAssignmentForm.tsx # Main creation form
│ │ ├── QuestionConfigSection.tsx # Question type config table
│ │ └── QuestionTypeRow.tsx # Single question type row (steppers)
│ ├── output/
│ │ ├── QuestionPaper.tsx # Full paper container
│ │ ├── PaperHeader.tsx # School/exam header
│ │ ├── SectionBlock.tsx # Paper section (MCQ, short answer, etc.)
│ │ ├── QuestionItem.tsx # Individual question display
│ │ ├── AnswerKeySection.tsx # Answer key
│ │ └── ActionBar.tsx # Print / Download / Back buttons
│ └── ui/
│ ├── Button.tsx # Reusable button (primary/secondary/danger/ghost)
│ ├── Toast.tsx # Toast notifications
│ ├── ThreeDotMenu.tsx # Context menu (view/delete)
│ ├── GenerationStartedModal.tsx # Modal on generation start
│ ├── GenerationCompleteToast.tsx # Toast on generation complete
│ └── LoadingSpinner.tsx # Spinner component
├── context/
│ └── ToastContext.tsx # Global toast state
├── hooks/
│ ├── useAssignments.ts # Assignment fetch/delete hooks
│ ├── useSocket.ts # Socket.io connection per page
│ └── useBackgroundSocket.ts # Background listener for generation events
├── lib/
│ ├── api.ts # Axios instance (withCredentials)
│ ├── socket.ts # Socket.io client init
│ └── pdf.ts # HTML → PDF utility
├── store/
│ └── useAssignmentStore.ts # Zustand — assignments, loading, generation status
└── types/
└── assignment.ts # TypeScript types
| Method | Path | Description |
|---|---|---|
| GET | /api/assignments |
List assignments (paginated + search) |
| POST | /api/assignments |
Create assignment + queue AI generation |
| GET | /api/assignments/:id |
Get assignment detail |
| DELETE | /api/assignments/:id |
Delete assignment |
| POST | /api/assignments/:id/regenerate |
Re-queue AI generation |
| Event (client → server) | Description |
|---|---|
subscribe:dashboard |
Join dashboard room for global updates |
subscribe:assignment |
Join room for a specific assignment's updates |
unsubscribe:assignment |
Leave assignment room |
| Event (server → client) | Description |
|---|---|
generation:processing |
AI job started |
generation:complete |
AI generation finished |
generation:error |
AI generation failed |
- Node.js 20+
- MongoDB Atlas URI
- Upstash Redis URL (TLS)
- OpenRouter API key — openrouter.ai
cd backend
npm installCreate backend/.env:
PORT=3000
MONGO_URI=your_mongodb_atlas_uri
REDIS_URL=your_upstash_redis_url
OPENROUTER_API_KEY=your_openrouter_key
FRONTEND_URL=http://localhost:8080The AI models are hardcoded as a fallback chain in
backend/config/openrouter.js(AI_MODELS). The worker tries each model in order and falls through to the next if one is removed/renamed (404), rate-limited, or returns bad output — so there's no single model to misconfigure.
npm run devcd frontend
npm installCreate frontend/.env.local:
NEXT_PUBLIC_BACKEND_URL=http://localhost:3000npm run devThe app runs at http://localhost:8080.
- Create assignment — User fills in the form (subject, class, question config). Frontend POSTs to
/api/assignments. - Async generation — Backend validates with Zod, saves a
pendingAssignment, and pushes a job onto the BullMQ Redis queue. The response returns immediately. - Background worker — Picks up the job, calls the OpenRouter AI API with a structured JSON prompt, parses the response, and saves the generated content to MongoDB. Emits Socket.io events throughout.
- Real-time updates — Frontend listens on Socket.io. When generation completes a toast appears with a link to the result.
- PDF output — The question paper is rendered to HTML and converted to a downloadable PDF via html2canvas + jsPDF.
- AI question paper generation — Configurable question types, marks, and counts
- Async job queue — Generation never blocks the UI; runs in the background
- Real-time notifications — Socket.io push when generation completes
- Dark / Light mode — Toggleable theme, persisted in localStorage
- PDF download & print — Print-optimized A4 layout
- Infinite scroll — Paginated assignment list
- Search — Debounced assignment search