Chapter 1
Codebase structure and quality
Nokio is not a monorepo. It is nine sibling GitLab projects under one workspace folder. Five surfaces run in production. Four are empty or a scaffold. Two eras of code coexist: extend the newer patterns; contain the older god files.
Workspace map
| Repository | Role | Maturity | Score |
|---|---|---|---|
backend | System of record API — Express, Sequelize/MySQL, Redis, OpenSearch, Socket.IO, in-process crons | Production | 4.5 |
web-app | Consumer web — Next.js 14 App Router, NextAuth, React Query, CSS Modules | Production | 6 |
mobile-app-new | iOS / Android — React Native 0.74, Redux Persist + React Query, Detox | Production | 5.5 |
aws / imdb_json | IMDb S3 → Lambda → Step Functions preprocess | Production (manual ops) | 7 |
data-ingestion | Python Excel → admin API scripts (new releases, upcoming, pick of the week) | Ops scripts | 4 |
nokio-batch-job-app | Intended NestJS batch worker | Scaffold | 2 |
data-services | Intended data services | Empty stub | 0 |
recommendation-services | Intended recommendation service | Empty stub | 0 |
devops | Intended ops templates | Empty stub | 0 |
Default branches on the main apps: develop (staging), main (production). Hosts from docs and CI: API app.nokioapp.com / staging-app.nokioapp.com; web nokioapp.com / staging-webapp.nokioapp.com. GitLab deploy jobs also reference api.nokio.com.
How the pieces connect
Solid lines are live request paths. Dashed line is daily IMDb diff ingest from S3 into API cron.
Two eras of code
Folders exist for routes → services → repositories → models. Legacy code skips that and talks to helpers and Sequelize from the route file. New work should land on Era B. Growing Era A files is the highest-cost change pattern in the workspace.
Era B — extend
backend/src/routes/web/*with ~48 DTOs, express-validator, cache middleware- Repositories on a few hotspots (~14 files)
- Web React Query key factories (~52 hooks) + OpenAPI model classes
- Feature flags; generated TypeScript clients on web and mobile
Era A — contain
movies.ts2227 LOC,admin.ts2180 LOC- 10 helper ↔ service basename collisions (user, movie, review, …)
- Mobile
Movie_DEPRECATEDstill routed (~20 files) - Fire-and-forget in-process crons; silent empty catches
Quality dimensions
| Dimension | Score | Notes |
|---|---|---|
| Client structure (web) | 7 | App Router, RQ key factories, CSS Modules |
| Typed API contract | 7 | OpenAPI → generated schema + model classes + override layers |
| Auth / secrets hygiene | 2 | Tracked secrets, weak hashing, JWT without DB revalidation |
| Backend layering | 4 | Folders exist; enforcement does not |
| Error handling / UX | 3 | ~28 web contexts return null on query error |
| Automated tests | 2 | Backend 2 files; web 4; mobile utils + Detox auth |
| Duplication control | 3 | Eight copies of useContactActionMutation on mobile |
Repository notes
backend — 4.5 / 10
About 435 TypeScript files under src, ~114 route files (~44k LOC), ~68 services, ~49 helpers, ~110 models, ~269 migrations, 2 test files. Fourteen files exceed 1,000 lines. TypeScript is strict in tsconfig, but production build is SWC only — there is no tsc gate in CI. README still says PostgreSQL; code uses MySQL.
Central defaultErrorHandler is a good baseline, undermined by empty catches, helpers that return e, and health endpoints that can leak raw errors.
web-app — 6 / 10
~495 TSX files, ~51% "use client", ~219 CSS modules, ~39 Storybook stories, 4 tests, zero App Router error.tsx. Strengths: prefetch + HydrationBoundary, OpenAPI models, virtualized lists, Husky pre-push (lint + tsc + test). Weaknesses: silent error UX, dual HTTP stacks that disagree on Authorization (Bearer / JWT / raw token), module-level mutable token state on the API client, NEXTAUTH_SECRET || "your-secret" fallback.
mobile-app-new — 5.5 / 10
~573 TSX + ~109 leftover JS files. Dual token storage (Redux Persist whitelist and AsyncStorage). Mid-migration: Redux for session/UI, React Query for server data. God screens (e.g. StoryContainer ~789 LOC). Folder typo Dashboad. Detox coverage of auth/onboarding is the strongest automated product net in the workspace. APNs private key is git-tracked.
aws / imdb_json — 7 / 10
Streaming Lambdas (imdb_split → imdb_diff → imdb_merge) with the best architecture README in the workspace. No in-repo IaC (manual AWS setup). Handlers often return err instead of throwing, which weakens Step Functions failure semantics.
data-ingestion — 4 / 10
Three near-duplicate Excel processors. Env-based JWT, retries, gitignored env files. No requirements.txt, no tests, sample Excel committed, README has machine-specific absolute paths.
Stubs
nokio-batch-job-app is still a Nest “Hello World”; readFile only logs the path. data-services, recommendation-services, and devops contain README + gitignore only. Azure pipeline templates elsewhere still reference a devops template repo that is empty here. Recommendations in this product are user-to-user rows, not a separate ML service — the empty recommendation repo should not be mistaken for a missing production dependency.
Critical security findings
| Finding | Where | Severity |
|---|---|---|
| Secrets tracked in git (env file with live-looking SendGrid / FCM / DB values) | backend | Critical |
| APNs signing key tracked | mobile-app-new | Critical |
| Password hashing: PBKDF2, 1 SHA-1 iteration, 128-byte key | backend/src/models/user.ts | Critical |
JWT strategy does done(null, payload) — no MySQL reload of the user | backend/src/config/passport.ts | High |
Public festival route reads req.user.id without mounting JWT auth | filmFestivalPublic.ts | High |
| Fire-and-forget cron jobs without await/catch | helpers/cron.ts | High |
Testing posture
| Repo | Automated net | Gap |
|---|---|---|
| backend | 2 unit files | Routes, cron, admin, chat uncovered |
| web-app | 4 files (mostly utils) | Auth, API, React Query, pages uncovered |
| mobile-app-new | Utils Jest + Detox auth | Home / Movie / Circles / Festival thin |
| aws / imdb_json | Local harness only | No CI tests |
| data-ingestion | None | — |
Clients have process (pre-push tsc/test) without enough coverage for those gates to catch domain bugs.
Working agreements for new code
| Do | Do not |
|---|---|
Add API endpoints under routes/web with DTOs and validators | Grow movies.ts / admin.ts |
| Share React Query hooks and key factories | Copy useContactActionMutation into another screen |
Render recoverable error UI / error.tsx | return null on isError |
Keep .env* and signing keys out of git | Commit credentials “for convenience” |
Run tsc in backend CI; await/catch crons | Assume an SWC build equals type safety |
| Document MySQL as the system of record | Trust the README’s PostgreSQL mention |