HUHEMS is a full-stack exam management system for Haramaya University. It supports admin-managed exams and question banks, student exam attempts with time limits, and reporting/analytics.
Author : Kenean Dita
backend/: Go API (Gin + GORM + PostgreSQL)frontend/: Next.js web app (React + Tailwind UI)
- Create and manage exams (settings, publish/unpublish, reports)
- Create questions manually or import questions in bulk via CSV
- Manage student accounts (create/edit/delete) and import students in bulk via CSV
- View analytics (exam-level and performance breakdowns)
- Start exam attempts with a pre-start rules/confirmation gate
- Countdown timer with auto-submit when time is up (client + server enforced)
- Flag/unflag questions during an attempt
- View attempt results with human-readable answer text
- Node.js 18+ (for the frontend)
- Go 1.21+ (for the backend)
- PostgreSQL 16 (or run it via Docker)
Required variables:
DB_URL(Postgres connection string)JWT_SECRET(used to sign auth tokens)PORT(defaults to8080)
Example (already present in backend/.env):
DB_URL=postgres://postgres:newpassword123@localhost:5432/huhems?sslmode=disable
JWT_SECRET=yourstrongsecret
PORT=8080NEXT_PUBLIC_API_BASE_URL(the backend base URL)
NEXT_PUBLIC_API_BASE_URL=http://localhost:8080From the repo root:
docker compose up -d --buildServices:
- Frontend: http://localhost:3000
- Backend health: http://localhost:8080/health
- Postgres:
localhost:5432
If Docker commands fail on Windows, ensure Docker Desktop is installed and running.
- Start Postgres (Docker):
docker compose up -d db- Backend:
cd backend
go run ./cmd/migrate
go run ./cmd/api- Frontend:
cd frontend
npm install
npm run devHUHEMS supports two bulk-import flows:
- Question import (inside an exam)
- Student import (in Student Manager)
Both imports accept a .csv uploaded as multipart form-data field name: file.
Import location:
- Admin → Exams → open an exam → Questions tab → Import Questions (CSV)
CSV columns:
| Column | Required | Description |
|---|---|---|
text |
yes | The question text |
type |
yes | single_choice or multi_choice (also accepts single, multi, sc, mc) |
choices |
yes | Pipe-separated choice list |
correct |
yes | Either correct indices or exact choice text(s) |
Use a pipe: |
A|B|C|DA | B | C | D(spaces are allowed; they are trimmed)
You can provide correct answers in either of these ways:
- 1-based indices into the
choiceslist:
- Single choice:
3 - Multi choice:
1|4(commas also work:1,4)
- Exact choice text(s) (case-insensitive match):
- Single choice:
Central Processing Unit - Multi choice:
Option A|Option C
Note: if your correct value is numeric (example 80), the importer treats it as an index only when it fits within the number of choices; otherwise it is treated as answer text and matched to a choice like "80".
text,type,choices,correct
"What does CPU stand for?",single_choice,"Central Processing Unit|Computer Personal Unit|Central Performance Utility|Control Processing Unit",Central Processing Unit
"Select prime numbers",multi_choice,"2|3|4|5","2|3|5"
"Which HTTP method retrieves data?",single_choice,"POST|PUT|GET|DELETE",3CSV tips:
- If your question text contains commas, wrap it in quotes.
- If you include a header row, columns can be reordered.
Import location:
- Admin → Students → Import Students (CSV)
CSV columns:
| Column | Required | Description |
|---|---|---|
username |
yes | Student username (must be unique) |
email |
yes | Student email (must be unique; must be a valid email) |
password |
yes | Initial password (min 8 characters) |
fullName |
yes | Student full name |
year |
yes | Numeric year/level (must be >= 1) |
department |
yes | Department name |
Header row is optional. If present, columns can be reordered.
username,email,password,fullName,year,department
student001,student001@huhems.local,Student123!,Jane Doe,1,Computer Science
student002,student002@huhems.local,Student123!,Abel Kebede,2,Information SystemsThis usually means the frontend cannot reach the backend URL configured by NEXT_PUBLIC_API_BASE_URL.
Checklist:
- Confirm backend is running:
- http://localhost:8080/health should respond.
- Confirm frontend is configured correctly:
frontend/.envshould containNEXT_PUBLIC_API_BASE_URL=http://localhost:8080
- Restart servers after changes:
- If you change backend routes/controllers, restart the backend process.
- If you change
frontend/.env, restart the Next.js dev server.
- If it’s from the backend: the backend route is missing or the backend wasn’t restarted.
- If it’s from the frontend: the Next.js API route may not be built/running.
- Change
JWT_SECRETfor real deployments. - Don’t use demo passwords in production.
- Consider running behind HTTPS and using secure cookies in production.
MIT — see LICENSE.