A gamified learning platform with AI-assisted content generation, course management, and a mobile-first learner experience.
Questify is a monorepo containing three apps — API (Express + Prisma), Mobile (Expo + React Native), Admin (Vite + React) — and a shared types package.
- Course, quest & stage system — multi-stage quests (informational, MCQ, boss battles) with progression tracking
- Gamification — XP, levels, streaks, badges (5 families), achievements, daily/weekly challenges
- Shop & inventory — purchasable items, equipped cosmetics, streak-freeze consumables
- AI content generation — generate courses, quests, and stages from prompts
- RAG-powered Q&A — ask AI about course content; documents/PDFs ingested into a vector pipeline
- Leaderboards with daily/weekly/all-time snapshots
- Admin dashboard — manage courses, users, badges, shop items, and analytics
- Notifications — push and transactional email (Resend)
- Auth — JWT + Google OAuth
┌─────────────────┐ ┌──────────────────┐
│ Mobile (Expo) │─────▶│ │
│ iOS / Android │ │ │
└─────────────────┘ │ API │ ┌──────────────────┐
│ Express + │─────▶│ PostgreSQL │
┌─────────────────┐ │ Prisma │ │ (Prisma ORM) │
│ Admin (Vite) │─────▶│ (port 8080) │ └──────────────────┘
│ React SPA │ │ │
└─────────────────┘ │ │ ┌──────────────────┐
│ │─────▶│ Cloudflare R2 │
│ │ │ (file storage) │
│ │ └──────────────────┘
│ │
│ │─────▶┌──────────────────┐
│ │ │ Resend (email) │
└──────────────────┘ └──────────────────┘
│
│── pg-boss job queue
| Layer | Technology |
|---|---|
| Mobile | Expo SDK 56, React Native 0.85, React 19.2.3, expo-router |
| Mobile state | Zustand, TanStack Query |
| Mobile UX | Reanimated 4, Gesture Handler, Lottie, expo-audio, expo-haptics |
| Admin | Vite 8, React 19.2.3, React Router 7 |
| Admin UI | Radix UI, Recharts, TanStack Table, React Hook Form |
| Backend | Express 5, Prisma 7, PostgreSQL 15+ |
| Auth | Passport.js (JWT + Google OAuth) |
| Validation | Zod 4 (shared across all apps via @questify/schemas) |
| AI | OpenAI SDK, Hugging Face, LangChain text splitters |
| Resend + React Email | |
| Job queue | pg-boss |
| File storage | Cloudflare R2 (prod) / local (dev) via multer + sharp |
| Testing | Vitest (API + Mobile + Admin) |
| Build | TypeScript 5.9, npm workspaces, Turborepo |
| Tool | Version | Why | Check |
|---|---|---|---|
| Node.js | 22+ | Workspace + Turbo | node -v |
| npm | 11.6.1 (pinned via packageManager) |
Repo uses npm workspaces — do not use pnpm/yarn | npm -v |
| PostgreSQL | 15+ | API database | psql --version |
| Xcode (macOS, iOS) | latest stable | iOS dev build | xcodebuild -version |
| Android Studio + emulator | latest stable | Android dev build | adb --version |
git clone https://github.com/linzi-v7/questify.git
cd questify
npm install
cp apps/api/.env.example apps/api/.env
cp apps/admin/.env.example apps/admin/.env
cp apps/mobile/.env.example apps/mobile/.envNever commit
.envfiles — they are in.gitignore.
API — apps/api/.env
| Var | Required | Purpose |
|---|---|---|
DATABASE_URL |
yes | postgresql://user:password@localhost:5432/questify |
JWT_SECRET |
yes | Long random string. Generate with openssl rand -hex 32 |
PORT |
no | Defaults to 8080 |
CORS_ORIGIN |
no | Defaults to http://localhost:5173 (Admin) |
Admin — apps/admin/.env
| Var | Required | Purpose |
|---|---|---|
VITE_API_URL |
yes | http://localhost:8080 in dev |
Mobile — apps/mobile/.env
| Var | Required | Purpose |
|---|---|---|
EXPO_PUBLIC_API_BASE_URL |
yes | API URL as seen from device. http://localhost:8080/api for emulator, your LAN IP for a physical device (e.g. http://192.168.1.4:8080/api) |
EXPO_PUBLIC_API_DEBUG_LOGGING |
no | Verbose API logs in dev. true / false |
For the full list of optional variables (Google OAuth, R2, Resend, RAG embeddings), see the comments in apps/api/.env.example.
npm run build:schemas # compile shared Zod schemas
npm run generate # generate Prisma client
npm run migrate # apply migrations
cd apps/api && npx prisma db seedThe seed creates a complete starter dataset: 3 users, 2 courses, multi-stage quests, 20 badge definitions, 10 achievements, 4 challenges.
Default credentials after seeding:
- Admin:
admin@questify.com/password123 - Students:
student@questify.com/password123,jane.smith@questify.com/password123
npm run dev # API + Admin + Mobile (Turborepo)Or run individually:
npm run dev:api # API → http://localhost:8080
npm run dev:admin # Admin → http://localhost:5173
npm run android # Mobile → Android dev build (first run)
npm run ios # Mobile → iOS dev build (first run)Once a dev build is installed on your device/emulator, subsequent JS changes hot-reload without a rebuild — see Mobile development build.
All commands run from the repo root unless noted.
| What | Command | Notes |
|---|---|---|
| Start everything | npm run dev |
Turbo — API + Admin + Mobile |
| Start one app | npm run dev:api / dev:admin / dev:mobile |
|
| Mobile native build (first run / native lib added) | npm run android / npm run ios |
Wraps expo run:* |
| Build everything | npm run build:all |
|
| Rebuild shared schemas | npm run build:schemas |
After changing packages/schemas/ |
| Generate Prisma client | npm run generate |
After schema changes |
| Run migrations | npm run migrate |
prisma migrate dev |
| Seed database (fresh) | cd apps/api && npx prisma db seed |
Wipes DB first |
| Seed database (additive) | cd apps/api && npx prisma db seed -- --keep |
Preserves existing data |
| Lint | npm run lint |
ESLint |
| Run tests | cd apps/api && npm test |
Vitest |
⚠️ This project uses native modules (reanimated, gesture-handler, lottie, expo-audio, react-native-svg, secure-store, vector-icons, …) and cannot run in Expo Go. You must use a development build.
npm run android # or: npm run iosThe first build compiles the native code for your target platform and installs the app on the connected device/emulator (~5–10 minutes). After that:
- JS changes hot-reload instantly — no rebuild required.
- Native dependency changes (e.g. adding a new
expo-*package) require a rebuild.
In apps/mobile/.env:
- Emulator on the same machine as the API →
EXPO_PUBLIC_API_BASE_URL=http://localhost:8080/api - Physical device on the same Wi-Fi → use your machine's LAN IP, e.g.
http://192.168.1.4:8080/api. Find it withipconfig(Windows) oripconfig getifaddr en0(macOS).
questify/
├── apps/
│ ├── api/ Express + Prisma REST API (port 8080)
│ ├── admin/ Vite + React admin dashboard (port 5173)
│ └── mobile/ Expo + React Native app
├── packages/
│ └── schemas/ @questify/schemas — shared Zod types
├── turbo.json
└── package.json workspace config
npm installat the root installs everything — one lockfile, onenode_modules.packages/schemas/is built first (npm run build:schemas) so consuming apps can pick up new types.turbo.jsonorchestrates build order across the workspace.
The .env.example files in each app are the source of truth, with inline comments and links for every variable. The README only documents the minimum required to run locally.
| App | File |
|---|---|
| API | apps/api/.env.example |
| Admin | apps/admin/.env.example |
| Mobile | apps/mobile/.env.example |
Production-only concerns (not exercised in dev):
DATABASE_URL→ managed Postgres (RDS, Cloud SQL, Supabase, Neon, etc.)RESEND_API_KEY+RESEND_FROM_EMAIL→ verified sending domainR2_*→ Cloudflare R2 bucket + access tokensGOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET/GOOGLE_CALLBACK_URL→ registered production OAuth clientAPP_PUBLIC_URL,EMAIL_LOGO_URL→ your public domain + CDN-hosted logoNODE_ENV=production,CORS_ORIGINset to the Admin origin
Run from apps/api:
cd apps/api
npx tsx src/scripts/createAdmin.ts # create an admin user (prompts for email/password)
npx tsx src/scripts/leaderboard-diag.ts # diagnose leaderboard state
npx tsx src/utils/importQuestStages.ts # bulk-import quest stages from a fileThe same scripts are also wired up as cli:create-admin and cli:leaderboard-diag in apps/api/package.json (npm run cli:create-admin -w apps/api).
| App | Command |
|---|---|
| Mobile (Expo) | cd apps/mobile && npx expo install <package> |
| API | npm install <package> -w apps/api |
| Admin | npm install <package> -w apps/admin |
| Shared schemas | npm install <package> -w packages/schemas |
| Root dev tool | npm install --save-dev <package> -w root |
Never edit package.json versions by hand. Run npm install from the root after any workspace change.
Questify is a standard three-tier app and deploys to any cloud that can host Node.js, static sites, and a managed Postgres.
API — Run npm run build:api and serve the compiled output (apps/api/dist) with a Node 22 process manager (systemd, pm2, Docker, or a platform PaaS). Provision a managed PostgreSQL 15+ instance and set DATABASE_URL. Run prisma migrate deploy against the production DB on each release. The job worker can run in-process in dev; for production, run npm run worker -w apps/api as a separate process. Required env: NODE_ENV=production, DATABASE_URL, JWT_SECRET, CORS_ORIGIN, plus the optional integrations (R2, Resend, Google OAuth) you want enabled.
Admin — Run npm run build -w apps/admin to produce a static SPA in apps/admin/dist. Serve from any static host (object storage + CDN, Nginx, Vercel, Netlify, Cloudflare Pages). Set VITE_API_URL at build time to the production API URL.
Mobile — Use Expo Application Services (EAS): configure eas.json, then eas build --platform android|ios produces signed binaries for the Play Store / App Store. Use EAS Update for OTA delivery of JS-only changes between releases. Configure EAS env vars to match your production API URL.
A minimal self-hosted setup (VPS, e.g. Hetzner) — Docker Compose with three services: api (Node), admin (nginx serving the static build), db (Postgres), plus Caddy/Nginx as a reverse proxy terminating TLS in front of the API. Object storage can be Cloudflare R2 or a MinIO container; email via Resend.
- Fork the repo and create a feature branch.
- Install as in Quick start.
- Make your changes. Keep PRs focused and small.
- Run
npm run lintandcd apps/api && npm testbefore opening a PR. - Use Conventional Commits for commit messages (
feat:,fix:,chore:, …). - If you change
packages/schemas/, runnpm run build:schemasand commit the generated output if your change is consumed by a built artifact.
rm -rf node_modules package-lock.json apps/*/node_modules apps/*/package-lock.json
npm installnpm run build:schemas
npm installThen restart the dev server.
- Confirm the API is running (
curl http://localhost:8080/healthor similar). - On a physical device, use your machine's LAN IP in
EXPO_PUBLIC_API_BASE_URL, notlocalhost. Phone and computer must be on the same Wi-Fi. - Clear the dev build's cache: in the dev menu, shake the device /
Cmd+M(iOS) /Cmd+M(Android emulator) → Reload.
npx kill-port 8081
npm run dev:mobile -- --clearIf npm run migrate complains about drift or a failed migration, inspect state with npx prisma migrate status -w apps/api. For a clean local DB, npx prisma migrate reset -w apps/api (this wipes the database) and re-run npx prisma db seed.
cd apps/mobile/ios
pod install --repo-update
cd ..
npx expo run:ios --cleanMIT — Copyright (c) 2025 Faris Osama.
Built on top of Expo, Prisma, Express, Vite, Zod, TanStack Query, and the broader open-source ecosystem. Email delivery by Resend. File storage by Cloudflare R2.