Let’s be real — running a Spring Boot backend at scale isn’t cheap. And I wasn’t ready to keep burning resources just for the sake of familiarity. I needed efficiency without the cost, performance without complexity, and a codebase that doesn’t feel like a jungle. That’s where NestJS stepped in — and surprisingly, it held up really well.
I’ve been working on an LMS (Learning Management System) platform for over a year now — my very first project since I started learning how to build websites and full-stack infrastructure. The frontend changed more times than a chameleon changes its colors 🦎 — seriously. In the early days, I was mostly handling frontend, but eventually, things shifted, and I had to take care of everything — frontend, backend, infra — solo.
I started from not even knowing Java Spring Boot to somehow pulling off a working backend. But soon enough, reality hit: Spring Boot wasn’t cheap — it was eating up 27–29% CPU on an EC2 t2.micro instance, even when there was zero traffic. I knew Node.js better and trusted its efficiency, so I began migrating small services to Node — guess what? CPU dropped to just 6–7%, and I could run 4–5 apps simultaneously with 40–50 concurrent users each, using RDS as the database.
But as requirements grew, editing Java Repositories and Entities constantly, verifying data consistency, and testing it all over again became a nightmare. It honestly felt like I was stuck in a backend hellfire 🔥. Especially when I realized that making schema changes in production without a proper migration system could cause unrecoverable data loss — that’s when I knew something had to change.
Another big shift came when the organization behind this LMS dropped a massive requirement — a completely new app with a scanner-based attendance system and advanced access control mechanisms. That’s when things scaled from “just migration” to “full-blown infra redesign.”
It was clear — I had to rethink the entire backend architecture. That’s when I started diving deep into open-source codebases like cal.com and realized what clean architecture actually looks like. Around the same time, a golden suggestion from Mr. Saurabh Khatri changed my whole vision:
“Drive the entire software through configurations.”
That hit different. I started building everything with developer experience in mind — a system so flexible that even a new dev could add routes, pages, and access control just by tweaking a config file.
But this wasn’t a regular RBAC system — this was a beast with 13–15 unique roles, each with different capabilities. Some users even had multiple roles across modules. Implementing this kind of dynamic access control wasn't just tough — it was a structural challenge.
That’s when we realized — merging both LMS apps was inevitable. But there was a catch:
The older Java backend used sequential BIGINT IDs
The newer NestJS app used UUIDs
And both systems had existing relational data we couldn’t afford to lose.
So we spun up an experimental DB to test relational integrity. Slowly, we started mapping tables one by one, dropped constraints strategically, and adopted a hybrid alphanumeric ID strategy for syncing old and new entities. We also collapsed multiple user tables into a single unified user schema, managing roles more cleanly than ever before.
This phase taught me the most important lesson:
"Database design and clarity in relations should be locked in during the initial phase. Period."
Of course, TypeORM added its own flavors of chaos — especially with its strict syncing behavior and cryptic error messages. But to counter that, we built a custom logger system to catch and track errors across prod/test environments — which turned out to be a lifesaver.
And finally, the typeorm-model-generator helped scaffold out new entity classes based on the legacy DB — giving us a solid ground to work from.
This entire journey wasn’t just about migrating from Java to Node — it was a journey of growth, ownership, and evolution. From writing my first API to orchestrating a modular, configuration-driven architecture that scales, I’ve realized something important:
Great systems aren’t built overnight — they’re shaped by the mistakes, rewrites, late-night debugging, and aha moments along the way.
I’m no longer just writing code. I’m designing frameworks, shaping developer experience, and building platforms that others can build upon. And this LMS project? It’s no longer just a project — it’s a foundation. A blueprint. For everything I’ll build next.
Top comments (0)