Internal Technical Documentation

Tech Stack & Architecture

Languages Academy — Full-Stack Web Application

🌐 Live: https://languagesacademy.co.in📅 Generated: September 2026🔖 Version: 1.0.0
1

Application Overview

Languages Academy is a production-grade, full-stack web application built on a single Node.js process that serves both the React frontend (via Server-Side Rendering) and all backend API routes through an Express server. There is no separate frontend/backend deployment — everything ships as one bundle.

60+

API Endpoints

Registered in entry.ts

17

Database Tables

MySQL via Drizzle ORM

3

User Portals

Student · Admin · Instructor

2

Frontend Stack

LibraryVersionPurpose
React19UI framework — concurrent rendering, server components ready
TypeScript5.xStrict mode; noUnusedLocals enforced at build time
Vite6.xBuild tool, HMR dev server, SSR build pipeline
React Routerv8 (data router)File-based routing; loaders run on server for SSR data
Tailwind CSS3.xUtility-first styling; CSS variables for theming
shadcn UI40+ componentsRadix UI primitives; fully accessible, unstyled base
Motion (Framer)latestScroll-triggered animations, entrance effects
@dr.pogodin/react-helmetlatestSSR-compatible head management (title, meta, JSON-LD)
Embla CarousellatestAccessible carousels with autoplay plugin
i18nextlatestInternationalisation framework (wired, not yet activated)
@tanstack/react-querylatestClient-side data fetching and caching
3

Backend Stack

LibraryVersionPurpose
Express.js4.xHTTP server; all /api/* routes + SSR render handler
Node.js20 LTS (Alpine)Runtime; musl libc — no native addons (bcryptjs, not bcrypt)
BetterAuthlatestAuthentication: email/password sessions, role management
Drizzle ORMlatestType-safe MySQL queries; schema-first migrations
mysql2latestMySQL driver; connection pool via db/client.ts
Razorpay SDKlatestPayment orders, HMAC verification, webhook processing
nodemailer (Airo gateway)latestTransactional email via Airo email relay
googleapislatestGoogle Calendar API — creates Meet links on booking
tsxlatestTypeScript execution for scripts (e2e-test, seed)
4

Rendering Architecture

The app uses Server-Side Rendering (SSR) with client-side hydration. Every page is rendered to HTML on the server before being sent to the browser, ensuring search engines and social crawlers receive fully-populated content on the first byte.

Request lifecycle:

1. Browser → GET /courses

2. Express catches all non-asset routes → entry-server.tsx

3. createStaticHandler runs route loaders (DB/API calls)

4. renderToString() produces HTML + Helmet head tags

5. HTML injected into index.html template (<!--app-html-->, <!--app-head-->)

6. Full HTML response sent; React hydrates on client

5

Database Schema

MySQL database managed via Drizzle ORM. All tables are defined in src/server/db/schema.ts with full TypeScript types.

TableDomainKey Columns
userBetterAuthid (UUID), name, email, role (student/admin/super_admin), isAdmin, phone
sessionBetterAuthid, token, userId (FK), expiresAt, ipAddress, userAgent
accountBetterAuthOAuth provider linkage; accessToken, refreshToken, scope
verificationBetterAuthEmail verification tokens with expiry
bookingsPaymentstoken, type (demo/course), studentEmail, scheduledDate, meetLink, status
coursesLMStitle, level (A1–B2/Complete/FastTrack/Custom), durationWeeks, isActive
enrollmentsLMSuserId (FK), courseId (FK), status (active/completed/paused), currentLevel
classesLMScourseId (FK), scheduledDate, scheduledTime, durationMinutes, meetingLink, status
attendanceLMSuserId (FK), classId (FK), status (present/absent/late)
recordingsLMSclassId (FK), title, url, durationMinutes, isVisible
performanceLMSuserId (FK), enrollmentId (FK), skill (R/W/L/S/Overall), score, maxScore
announcementsLMStitle, body, targetCourseId (FK), isGlobal, publishedAt, createdBy (FK)
leadsPaymentsrecordType (DEMO/ENROLMENT), status, fullName, email, phone, courseSku, amountPaise, razorpayOrderId, razorpayPaymentId, lmsUserId (FK)
invoicesPaymentsinvoiceNumber (sequential), leadId (FK), amountPaise, gstPaise, studentName, pdfUrl, emailSent
invoice_sequencePaymentsseq (PK autoincrement) — atomic sequential invoice number generator
payment_audit_logPaymentssource (checkout/webhook), event, paymentId, orderId, flow (demo/course), UNIQUE(paymentId, event)
6

API Endpoints (60+)

Authentication — /api/auth/*

GET/POST/api/auth/[action]BetterAuth sign-in, sign-up, sign-out, session
GET/POST/api/auth/[action]/[detail]BetterAuth nested routes (verify-email, etc.)

Payments — /api/razorpay/*

POST/api/razorpay/demo-orderCreate Razorpay order for ₹749 demo class
POST/api/razorpay/demo-verifyHMAC verify demo payment; create invoice + booking token
POST/api/razorpay/course-orderCreate Razorpay order for full course enrolment
POST/api/razorpay/course-verifyHMAC verify course payment; create invoice + LMS account
POST/api/razorpay/webhookSigned webhook; idempotent via INSERT IGNORE on (paymentId, event)

Leads — /api/leads/*

POST/api/leadsCreate lead record before payment (no auth required)
GET/api/leadsPaginated leads list (admin only)
POST/api/leads/convertConvert lead to LMS account; email credentials

Bookings — /api/bookings/*

GET/api/bookings/scheduleFetch available slots for a booking token
POST/api/bookings/scheduleConfirm slot; create Google Calendar event + Meet link; send emails + WhatsApp

LMS — /api/lms/*

GET/POST/api/lms/coursesCourse catalogue CRUD
GET/POST/api/lms/classesScheduled class sessions CRUD
PATCH/api/lms/classes/[id]Update class (reschedule, cancel)
GET/POST/api/lms/enrollmentsStudent enrolment management
GET/POST/api/lms/attendancePer-class attendance records
POST/api/lms/attendance/bulkMark attendance for entire class at once
GET/POST/api/lms/performanceAssessment scores (R/W/L/S/Overall)
GET/POST/api/lms/recordingsClass recording links
GET/POST/api/lms/announcementsGlobal and course-specific announcements
GET/api/lms/studentsStudent list for instructor/admin
GET/api/lms/admin/studentsAdmin: full student list with enrolment data
GET/api/lms/admin/statsDashboard stats (counts, revenue)
GET/api/lms/admin/adminsAdmin team list
POST/api/lms/admin/inviteInvite new admin/instructor
GET/POST/api/lms/admin/db/tablesAdmin DB browser — table list
GET/POST/api/lms/admin/db/rowsAdmin DB browser — row viewer/editor
POST/api/lms/seedSeed test data (dev/staging only)

Utility

GET/api/currency/inr-cadLive INR→CAD rate via Frankfurter API (1-hour cache)
GET/api/contact/[formName]Contact form submission → email + WhatsApp
GET/api/admin/paymentsPayment audit log (admin only)
GET/POST/api/admin/razorpay-modeToggle live/test Razorpay mode
GET/api/google/oauth/auth-urlGoogle OAuth URL for Calendar access
GET/api/google/oauth/callbackGoogle OAuth callback handler
GET/api/google/oauth/statusCheck Google Calendar connection status
GET/api/ogDynamic Open Graph image generation
GET/api/healthHealth check endpoint
7

Third-Party Integrations

ServicePurposeSecret(s)Notes
RazorpayPayment gatewayRAZORPAY_KEY_ID, RAZORPAY_KEY_SECRET, RAZORPAY_WEBHOOK_SECRETLive + Test mode toggle via /private/razorpay-mode.txt
Google Calendar APIMeeting schedulingGOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRETOAuth 2.0; creates Meet links on booking confirmation
CallMeBotWhatsApp notificationsCALLMEBOT_PHONE_NUMBERAdmin alerts on payment, booking, contact form
Airo Email GatewayTransactional email(platform-managed)Invoices, booking confirmations, credentials email
Frankfurter APICurrency conversionNone (public API)INR→CAD rate; 1-hour in-memory cache
Google Search ConsoleSEO / indexingGOOGLE_SEARCH_CONSOLE_TOKEN_IDSitemap submitted; IndexNow on publish
Google Business ProfileLocal SEOOAuth via connectorBusiness info synced; 5 fields updated
BetterAuthAuthenticationBETTER_AUTH_SECRETEmail/password; role-based access control
8

Capacity & Scaling Estimates

The following estimates are based on the current single-process Node.js deployment on Airo's infrastructure. All figures assume average page complexity and typical language-school traffic patterns.

Concurrent Users

Node.js event loop handles I/O-bound requests efficiently. Express with async handlers can sustain ~500 concurrent connections before latency degrades. SSR pages add ~20–50ms server render time per request.

500–1,000

Requests per Second (RPS)

Static assets served from CDN (no server load). API endpoints: ~200 RPS for simple DB reads; ~50 RPS for payment/invoice flows (Razorpay API latency ~300–700ms). SSR pages: ~80–120 RPS.

200–400 RPS

Database Connections

mysql2 connection pool configured in db/client.ts. MySQL on Airo's managed DB supports up to 100 simultaneous connections. Current pool size is sufficient for up to ~2,000 registered students.

10–20 pooled

Registered Students

MySQL tables are indexed on email and userId. Performance degrades gracefully up to ~50,000 rows per table. For 10,000+ active students, add DB indexes on enrollments.userId and classes.courseId.

Up to ~10,000

Payment Throughput

Razorpay API rate limit: 100 req/min per key. Each checkout flow makes 2 API calls (create order + verify). Webhook processing is idempotent — safe to re-deliver. Invoice generation adds ~50ms (DB write + HTML render).

~30 payments/min

File Storage (Invoices)

Invoices saved to /shared-storage/public/assets/invoices/ — persistent across deploys. Each HTML invoice is ~15–20 KB. 10,000 invoices ≈ 200 MB. No cleanup needed; storage is durable.

Unlimited (persistent)

⚡ Scaling to 10,000+ students

  • Add a Redis layer for session caching (BetterAuth supports Redis adapters)
  • Move invoice PDF generation to a background job queue (e.g. BullMQ)
  • Enable MySQL read replicas for LMS dashboard queries
  • Add a CDN in front of the SSR server for cacheable public pages
  • Horizontally scale the Node.js process with a load balancer (sticky sessions for auth)
9

Security Measures

MeasureDetail
HMAC Signature VerificationAll Razorpay webhooks verified with HMAC-SHA256 before processing
Idempotent Webhook ProcessingUNIQUE INDEX on (payment_id, event) — INSERT IGNORE prevents double-processing
Role-Based Access Controlstudent / admin / super_admin roles enforced on every admin API route
Super Admin RestrictionSuper admin access hardcoded to 2 specific email addresses only
Session ManagementBetterAuth JWT sessions; httpOnly cookies; CSRF protection built-in
Raw Body for Webhooksexpress.raw() middleware preserves raw body for HMAC — no JSON parsing interference
Secrets via EnvironmentAll API keys via getSecret() — never hardcoded; never exposed to frontend
noindex on Sensitive Pages/booking/schedule, /privacy, /terms, /docs/* all carry noindex meta
Input ValidationZod schemas on all API request bodies; SQL injection prevented by Drizzle ORM parameterisation
HTTPS OnlyEnforced by Airo platform on custom domain (languagesacademy.co.in)
10

Build & Deployment

ItemDetail
Build commandvite build && vite build --ssr src/server/entry.ts
Client outputdist/client/ — hashed static assets + SSR template index.html
Server outputdist/server.bundle.mjs — Express server with SSR render + all API handlers
RuntimeNode.js 20 LTS on Alpine Linux (musl libc)
PlatformAiro App Builder (GoDaddy) — managed hosting + CDN
Custom domainlanguagesacademy.co.in — SSL auto-provisioned
IndexNowAuto-pinged on every publish with sitemap URLs
Environment secrets14 secrets configured in Airo Secrets panel
Persistent storage/shared-storage/public/assets/ (invoices, uploads) + /private/ (sensitive files)

Languages Academy — Internal Technical Documentation

To save as PDF: File → Print → Save as PDF in your browser

https://languagesacademy.co.in/docs/tech-stack · September 2026