- Backend: NestJS (TypeScript) API with Prisma ORM, Prisma schema in
backend/prisma/schema.prisma. - Frontend: React + Vite (TypeScript) UI in
frontend/. - Tests:
- Integration tests in
integration-test/(Postman collection + Newman runner) - Load & performance tests in
load-test/(k6 scripts)
- Integration tests in
- Infrastructure: Dockerfile(s) under
backend/and GitHub Actions workflows for CI/CD.
- Backend: NestJS 10, Postgres (Prisma), JWT auth, Passport, Socket.IO, Puppeteer for PDF generation.
- Frontend: React 19, Vite, Tailwind, React Router, SWR, i18n.
- Hosting: AWS Amplify (frontend) + Amazon EC2 (backend) with Route 53 DNS management.
- Storage & Integrations: S3 (AWS SDK), Cloudinary, SendGrid.
- Testing: Postman + Newman (integration), k6 (load/performance), OWASP ZAP (security scanning).
- CI/CD: GitHub Actions (sequential workflow: performance-security-scan → production-deploy)
Users
↓
[Route 53] (DNS Management)
↓
┌─────────────────────────────────────┐
│ AWS Amplify │
│ Frontend (React + Vite) │
│ - Global CDN distribution │
│ - Automatic deployments │
│ - SSL/TLS certificates │
└──────────────────┬──────────────────┘
│ HTTPS/WebSocket
↓
┌─────────────────────────────────────┐
│ Amazon EC2 │
│ Backend (NestJS API) │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Application Layer │ │
│ │ ┌─────────────┬─────────────┐ │ │
│ │ │ Auth/JWT │ Socket.IO │ │ │
│ │ └─────────────┴─────────────┘ │ │
│ │ ┌─────────────┬─────────────┐ │ │
│ │ │ Modules │ Services │ │ │
│ │ │ (Quiz,User, │ (Prisma, │ │ │
│ │ │ Exercise) │ Puppeteer) │ │ │
│ │ └─────────────┴─────────────┘ │ │
│ └─────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Database Layer │ │
│ │ PostgreSQL │ │
│ │ (via Prisma ORM) │ │
│ └─────────────────────────────────┘ │
└──────────────────┬──────────────────┘
│
┌────────────┼────────────┐
│ │ │
↓ ↓ ↓
┌─────────────┐ ┌─────────┐ ┌─────────────┐
│ Amazon │ │ SendGrid│ │ Cloudinary │
│ S3 │ │ (Email) │ │ (Images) │
│ (File Store)│ └─────────┘ └─────────────┘
└─────────────┘
- User Access: Users access the application through Route 53 DNS, which routes traffic to AWS Amplify
- Frontend Delivery: Amplify serves the React application with global CDN for optimal performance
- API Communication: Frontend communicates with NestJS API on EC2 via HTTPS/WebSocket
- Authentication: JWT-based authentication with role-based access (TEACHER/STUDENT)
- Real-time Features: Socket.IO enables real-time notifications and live features
- File Storage: S3 for document storage, Cloudinary for image processing
- Email Services: SendGrid handles notifications and reports
- PDF Generation: Puppeteer running on EC2 generates PDFs for exercises and quizzes
GitHub Repository
↓
Performance & Security Scan
(k6 load tests + ZAP security scan)
↓
If scan succeeds
↓
Production CI/CD
┌─────────────────────────────────────┐
│ GitHub Actions │
│ │
│ 1. Build Docker image │
│ 2. Run integration tests │
│ 3. Push image to Docker Hub │
│ 4. SSH deploy to EC2 │
│ 5. Run database migrations │
│ 6. Start production container │
└──────────────────┬──────────────────┘
│
↓
┌─────────────────────────────────────┐
│ Amazon EC2 │
│ Production Environment │
│ - Docker container orchestration │
│ - Environment variable management │
│ - SSL/TLS termination │
└─────────────────────────────────────┘
| Layer | Technology (primary) | Notes |
|---|---|---|
| Frontend Hosting | AWS Amplify | Global CDN, automatic deployments, SSL certificates, branch-based environments |
| Backend Hosting | Amazon EC2 | Docker container deployment, auto-scaling capabilities, security groups |
| DNS Management | Amazon Route 53 | Domain routing, health checks, DNS failover |
| Backend framework | NestJS 10 | Modular controllers/services, global validation pipe, Swagger at /api/docs |
| ORM / DB | Prisma + PostgreSQL | backend/prisma/schema.prisma describes domain models (User, Quiz, Exercise, Document, IELTS, Submissions) |
| Auth | JWT, Passport | @nestjs/jwt, passport-jwt used for auth and role-based access (Role: TEACHER/STUDENT) |
| Frontend | React 19 + Vite | frontend/src with contexts, hooks, and pages; i18n configured (frontend/src/i18n) |
| Real-time | Socket.IO | For notifications / live features |
| File processing | Puppeteer, sharp | PDF generation and image processing |
| Storage | AWS S3 (SDK), Cloudinary | File uploads/document hosting |
| SendGrid | For notifications and reports | |
| Testing | Jest, Supertest, Postman/Newman | Unit, e2e, and integration testing in CI |
- Authentication & Authorization: JWT tokens,
authmodule, protected routes. - Users: listing, stats, roles (teacher/student), profile.
- Quizzes: create/manage quizzes, questions, submissions, grading, statistics.
- Exercises: create/manage exercises, student submissions, file attachments.
- Documents: upload and serve document library (PDFs, presentations, etc.).
- IELTS modules: Reading and Writing tests, teacher review workflows, AI/human scoring structure for writing.
- PDF generation: endpoints to render exercise/quiz PDFs (uses Puppeteer).
- Uploads: S3/Cloudinary integration, file metadata management.
backend/- NestJS applicationsrc/main.ts- app bootstrap, Swagger, global pipes, middlewaresrc/app.module.ts- module composition (Auth, Users, Quiz, Exercise, Document, IELTS, PDF)prisma/schema.prisma- DB schema and domain modelsscripts/- helper scripts (seed, drop-db)Dockerfile- container image for backend
frontend/- React + Vite appsrc/- App, pages, components, contexts, hooksvite.config.ts- Vite configuration (if present)
integration-test/- Production-safe API integration testspostman_collection.json- Postman collection (Newman runnable)postman_environment.json- Postman environment variables
load-test/k6/- Performance and load testingload-test.js- k6 load test script
.github/workflows/- GitHub Actions CI/CDperformance-security.yml- Performance and security checks (k6, ZAP)cicd_prd.yml- Production CI/CD pipeline (build, integration test, push, deploy)
Prerequisites: Node.js (>=18), pnpm/npm, Docker (for simulating production), PostgreSQL (or a remote DB), and npx.
Backend (from backend/):
cd backend
npm install
# build
npm run build
# run dev
npm run start:devFrontend (from frontend/):
cd frontend
npm install
npm run devDatabase & Prisma:
cd backend
# run migrations from fresh
npm run migrate:fresh
# or push (non-migration) schema
npm run migrate
# seed test data
npm run seedAPI docs (after backend started):
- Swagger UI:
http://localhost:3000/api/docs
Environment variables (important)
DATABASE_URL- PostgreSQL connection stringJWT_SECRET,JWT_ACCESS_EXPIRES_IN,JWT_REFRESH_EXPIRES_IN- auth configAWS_S3_*,CLOUDINARY_*- file storageSENDGRID_API_KEY- emailPORT- server port
See .github/workflows/deploy.yml for an example test.env assembled during CI (read-only production DB usage for tests).
-
End-to-end / production-safe API tests: Postman collection in
integration-test/postman_collection.json. -
Load & performance testing: k6 scripts in
load-test/load-test.js. -
CI uses GitHub Actions to build a Docker image, run it (with a read-only production DB URL supplied via secrets), wait for health to pass, then run Newman tests. Failing assertions cause the job to fail and upload
newman-results.jsonas an artifact.
The CI/CD pipeline consists of two GitHub Actions workflows that run sequentially:
Runs performance and security tests before deployment:
- Checkout repository.
- Build Docker image for the backend using
backend/Dockerfile(tag:tutorize-backend:perf). - Start a test container exposing the API on
localhost:3000. - Wait and poll the
/api/v1/healthendpoint until ready. - Run k6 load tests with configurable VUs and duration, output JSON results.
- Upload k6 results as artifacts.
- Run OWASP ZAP baseline security scan using GitHub Action.
- Upload ZAP security report as artifacts (JSON, HTML, Markdown, ZIP).
- Stop test container.
Triggers: Manual dispatch or push to master.
The main deployment pipeline that runs after performance-security scan completes successfully:
- Checkout repository.
- Build Docker image for the backend using
backend/Dockerfile(tag:tutorize-backend:test). - Prepare a
test.envfile in the runner with important production-like environment variables (in CI this pulls values from GitHub Secrets such asPROD_DATABASE_URL,JWT_SECRET,TEST_USER_*). The workflow intentionally usesREAD_ONLY_MODE=truefor safety when pointing at production DB. - Start a test container exposing the API on
localhost:3001. - Wait and poll the
/api/v1/healthendpoint until ready (with retry loop). - Install Newman and run the Postman collection from
integration-test/against the running container. The workflow exportsnewman-results.jsonand uploads it as an artifact. - If tests pass, build and push the Docker image to Docker Hub, then SSH to EC2 and deploy (pull image, run migrations, start container).
Triggers: Completion of performance-security workflow (sequential execution).
Push to master
↓
Performance and Security Scan
(k6 load tests + ZAP security scan)
↓
If scan succeeds
↓
Production CI/CD
(integration tests + build + deploy)
Copyright (c) 2025 An Nguyen. All Rights Reserved.