Chapter 2
Technical architecture
One Express process is the system of record. Clients, Excel ops, and the IMDb preprocess pipeline all converge there. Recommendations are user-to-user (or circle) rows with an optional review and chat thread — not a standalone ML service. The empty recommendation-services repo is not a missing production dependency.
Container map
Live runtime. data-services, recommendation-services, devops, and nokio-batch-job-app are omitted because they do not run in production.
Public hosts
| Environment | API | Web |
|---|---|---|
| Production | app.nokioapp.com · api.nokio.com | nokioapp.com |
| Staging | staging-app.nokioapp.com · api.staging.nokio.com | staging-webapp.nokioapp.com |
Mobile env templates point at staging-app / staging-webapp. Deploy is GitLab CI + PM2; Azure pipeline templates are also present. IMDb Lambdas are operated manually (no in-repo IaC).
Request path
- Client Mobile axios or web fetch. Base URL is
SERVER_URL/apiorNEXT_PUBLIC_API_URL/api. - Header Authorization uses scheme
jwt <accessToken>on the API. Web NextAuth stores backend tokens; mobile uses Redux persist plus AsyncStorage. Web HTTP stacks still disagree on Bearer vs JWT vs raw token. - Express morgan → body-parser (50mb) → fileupload → cors → correlationId. Mounted at
/api. - Passport JWT Strategy extracts scheme
jwt. Payload{ id, userType, region }becomesreq.user— not reloaded from MySQL. - Handler Legacy fat routes call helpers/models. Newer
/weband/v2use DTOs, services, and a few repositories. - Stores Sequelize/MySQL is the system of record. Redis caches some models. OpenSearch serves movie search. Optional Socket.IO emit or FCM.
Service catalog
| Unit | Stack | Talks to |
|---|---|---|
| backend | Express, TypeScript ~4.7, Sequelize, node-cron, Socket.IO, small Next 13 in the same repo | MySQL, Redis, OpenSearch, S3, FCM, SendGrid, TMDB, JustWatch, OAuth IdPs |
| web-app | Next.js 14 App Router, NextAuth, TanStack Query, CSS Modules, pnpm | HTTP /api only |
| mobile-app-new | RN 0.74, React Navigation 6, Redux Persist, React Query, Firebase, Sentry, Mixpanel | HTTP + Socket.IO /sockets + FCM |
| data-ingestion | Python scripts, JWT to admin/content APIs | backend admin routes |
| aws / imdb_json | Lambda, Step Functions, EventBridge, S3 | S3; backend cron pulls diffs |
Domains
The core domain is a social graph around movies. Catalog and editorial content (new releases, upcoming, pick of the week, trailers, festivals) is mixed with user-generated reviews, circles, recommendations, stories, and chat.
| Entity | Anchors | Notes |
|---|---|---|
| User | platforms, circles, sessions, FCM, prompts | Types GU / PPU / GUEST / WEB / admin |
| Movie | genres, persons, platforms, slugs, festivals | IMDb ingest + TMDB / JustWatch enrichment |
| Review | user, movie, circles, reactions, stories | Chat messages hang off reviews |
| Circle | owner, members, reviews, optional festival | Group feed + group chat room |
| Recommendation | from/to user, circle, movie, review | Thumbs-up + chat messages |
| Watchlist | user, movie, platform, newRelease | Release notifications target this |
| FilmFestival | categories, movies, nominations, circle | Public web + Next pages in the API repo |
| Story / ReviewStory | user, review | Home surface on web and mobile |
| Notification / ChatMessage | users, circle, review, movie, rec | HTTP + Socket.IO + FCM |
Cross-client coverage
| Domain | Web | Mobile | Backend home |
|---|---|---|---|
| Home / catalog | Region home, new-releases, trailers | Home tab | /web, /newrelease, /upcomingMovie |
| Movie detail | movie/[slug] | Movie stack (incl. Movie_DEPRECATED leftovers) | /movies, /web/movie, /movie-slugs |
| Circles | circles/* | Circles tab + SingleCircle | /circles, /v2/circles, /web/circles |
| Recommendations | my-recommendations, profile recs | Library + RecommendMovie | /users/recommendations*, /web/recommendations |
| Chat | Recommendations-with-chat HTTP | Socket.IO + CommentThread | /chat + /sockets |
| Festivals | festivals/* | FestivalList, Festival, PanelDiscussion | /film-festival* + Next src/app/festival |
| Stories | Home Stories carousel only | Create, view, react, comment | /stories |
| Auth / profile | NextAuth + my-profile / edit-profile | SignIn/Up + Settings | /, /users, /v2, /users/profile |
Data stores and pipelines
| Store | Config | Used for |
|---|---|---|
| MySQL | dialect mysql, :3306 — README still says Postgres | System of record · ~269 migrations |
| Redis | ioredis :6379 lazyConnect | Optional model/app cache — API still runs without it |
| OpenSearch | Movie search index | Title search |
| S3 | IMDb diffs and uploads | Preprocess output; cron ingest |
IMDb pipeline
Large IMDb JSON is split, diffed, and merged in Lambdas, then stored on S3. Backend cron pulls diffs. Streaming gzip/readline design is the strongest engineering in the workspace. No IaC in-repo.
Editorial Excel
Operators run Python against excels/ and POST to admin APIs: new releases, upcoming, pick of the week, trailers, streaming info. This is how catalog rails stay current between IMDb dumps.
Core schema
About 105 Sequelize models. The diagram is the social-movie core. Click a box to open columns, keys, relations, validations, and any MySQL procedures. The full 21-table catalog is on Schema.
Accent boxes are the two write-heavy social objects. Click a box for the detail panel. Junction tables and procedures are on Schema.
Notable schema facts
- Movies are keyed by IMDb id, not an internal surrogate; web URLs use
MovieSlugs. - v1 refresh tokens live on User; v2 uses hashed rows in UserSessions.
- Editorial tables (NewReleases, UpcomingMovie, PickOfTheWeeks) are written by Excel ingestion, not by IMDb cron.
- FilmFestival can own a Circle — festival social on mobile is a circle join, not a separate graph.
API surface
Express mounts a large OpenAPI-documented surface at /api. Live docs: express-swagger-generator (/api-docs.json). Clients regenerate TypeScript from that contract, then apply local schema.ts overrides. Dual auth generations exist: v1 opaque refresh on User, v2 session + hashed refresh. Request and response samples for 24 representative endpoints are on API.
| Group | Auth mix | What it covers |
|---|---|---|
| Auth | Public + v2 | email/password, Google, Apple, Facebook, refresh, forgot/reset, email confirm, app-version gate, account delete |
| Users | JWT / v2 | register, profile, onboarding, platforms, languages, PPU convert, FCM token, favorites, review reactions |
| Movies | JWT | search, slug lookup, homepage rails, reviews CRUD, threads, reactions, recommend, trailers, seasons |
| Social | JWT | contacts, circles, followers, recommendations-with-chat, mute notifications |
| Chat | JWT + sockets | HTTP chat plus Socket.IO open/close/post/react; offline peers get FCM |
| Catalog | JWT / admin | newrelease, upcomingMovie, pick-of-the-week, trailers, streaming-info |
| Festival | JWT + public | types, programs, nominations, schedule, panel; public web routes |
| Web public | Mostly public + cache | Newer /web/* DTO layer used by the Next.js app |
| v2 | Session tokens | login/refresh/logout, register, PPU, homepage filters |
| Admin | Role-gated | content ops, Excel landing endpoints, god-file admin.ts |
Representative endpoints
| Method | Path | Auth | Note |
|---|---|---|---|
| POST | /login | Public | email/password → access + v1 refresh |
| POST | /v2/login | V2 | session + hashed refresh |
| POST | /google-login · /apple-login · /facebook-login | Public | IdP tokens |
| POST | /users | Public | register |
| GET/PATCH | /users/profile | JWT | own profile |
| POST | /v2/users/ppu | V2 | create public-profile user — web has no UI |
| GET | /movies/v2/{slug} | JWT | web movie page |
| POST | /movies/{id}/review | JWT | create/update review |
| POST | /movies/{id}/recommendations | JWT | recommend a title |
| GET | /users/contacts | JWT | network |
| GET | /web/movie/{slug} | Web public | Era B DTO movie |
| POST | /account/delete-user | Public* | account deletion — web has no UI |
The OpenAPI schema already exposes notifications, PPU registration, account delete, circle mutes, and push schedule. Mobile consumes them. Web does not. Closing those gaps is UI work, not a new backend.
/web or /v2 with DTOs — not into movies.ts or admin.ts.