🔬 NestJS vs Go: What the data says abou ...

🔬 NestJS vs Go: What the data says about when to use each

Mar 08, 2026

imageHello everyone. Today I'm sharing the results of a benchmark I did to try and answer a question that comes up a lot in backend teams: does it make sense to migrate to Go, or is NestJS enough?

There is no magic answer. But data helps make better decisions.

🇪🇸 Lee este post en Español

Watch the full presentation here (Google Drive)


🎯 Why this project?

Go has a steep learning curve. It's not TypeScript, it lacks the NestJS ecosystem, and hiring Go developers is more expensive. So, before recommending something like this to a team or business, I needed real data that would answer when the effort is worth it, and when it isn't.

The idea wasn't "Go wins, NestJS loses." It was to understand in what contexts each one shines, and when the cost of adopting Go is justified.

🏗️ Benchmark Architecture

I developed a full benchmarking suite with four components orchestrated via Docker Compose on an isolated internal network:

  1. NestJS App (Node.js 22): DDD + Prisma v6. The productivity-oriented approach.

  2. Go App (Golang 1.24): Clean Architecture + GORM. The performance-oriented approach.

  3. Dashboard (Next.js 16): Acts as a BFF (Backend-for-Frontend) and load injection engine. It allows simulating how a real full-stack application behaves under stress — something closer to production than using k6 directly.

  4. PostgreSQL 15: Shared database. Both services point to the same instance and table, with UUIDs generated at the application layer for a fair comparison.

🛠️ Methodology

Each server internally measures the time from receiving the request until it confirms the insertion in PostgreSQL (processing_time_ns), eliminating network noise. The reported metrics are latency percentiles (p95), not averages, as they better expose Garbage Collector spikes and the actual stability of the system.

Two load schemes:

  • RPS Mode (Open System): Fixed RPS, independent of the backend state — simulates saturation.

  • Parallel Mode (Closed System): Concurrent virtual users that wait for the response before sending the next request.


💻 Execution Environment

All tests were executed on the same local machine, with all containers running simultaneously via Docker Compose. No isolated server infrastructure was used.

image

⚠️ Important: The fact that all services run on the same machine means they compete for the same physical resources (CPU, memory, disk I/O). This favors conservative results — in a dedicated server environment, Go's advantage in latency and throughput would be even more pronounced.


📊 Results by Scenario

🔵 Small Data — RPS Mode

Representative run: 500 RPS / 60s / Ramp-up 3s

image

📌 With moderate load and small payloads, the difference is real but small (~10%). Both 100% success rate. In this scenario, the argument to migrate to Go is weak from a business standpoint.


🟠 Small Data — Parallel Mode

Representative run: 50 vUs / Ramp-up 5s / Duration 60s

image

📌 With 200 vUs, Go's advantage widens (~15-20% better server time), but NestJS remains stable. The gap alone does not justify a migration.


🟡 Large Data — RPS Mode

Representative run: 100 RPS / 60s / Ramp-up 10s

image

⚠️ At 100 RPS with bulk inserts, the Prisma connection pool started to saturate (error P2024). NestJS lost ~15-18% of requests. Go processed 100% without errors.

At 200 RPS, NestJS fell to ~50% success rate. At 300 RPS, to ~25-30%. Go held up to ~92% with occasional SLOW SQL (~337ms).


🔴 Large Data — Parallel Mode

Representative run: 10 vUs / Ramp-up 5s / Duration 60s

image

📌 With low concurrency, both survive well. At 50 vUs, NestJS latency reaches ~550ms in P95. Go maintains ~320ms. The difference in bulk operations under pressure is where Protobuf + Go creates the biggest gap.


⚠️ Errors I Found (and what they tell us)

During the Large Data tests, not everything was perfect. These are the recorded errors:

image

Error Conclusion: the problem isn't Go vs NestJS, it varies because Prisma has a less efficient connection pool model than pgx/GORM under heavy concurrent load. Go had no pool errors in any stable run.


🤔 When Does the Equation Change?

This is the core point I wanted to explore. The data shows that there is no one-size-fits-all answer.

When NestJS is Enough (and the right answer)

  • Standard business operations, CRUD, institutional REST APIs

  • Small payloads with moderate load (< 300 RPS, < 100 vUs)

  • Small teams with an intense product roadmap — TypeScript/NestJS DX wins

  • MVPs, startups validating products — no reason for Go's overhead

  • Projects where the team lacks Go experience (and there's no budget to acquire it)

When Go Starts to Justify Itself

  • Bulk operations at scale (massive inserts, streaming, ETL)

  • Real-time data ingestion where Node.js GC spikes are problematic

  • Services with very strict p99 latency SLAs (Go has much more predictable GC pauses)

  • Systems expected to scale to thousands of concurrent connections without resizing infrastructure

  • The team already has Go developers or there is budget/time to train them

The Business Argument (the most important one)

Go has real costs that must be acknowledged:

  • Steep learning curve: concurrency with goroutines, explicit error handling, lacking mature generics until recently, a smaller ecosystem

  • More expensive and difficult hiring: Go developers are less common than TypeScript developers

  • More code to do the same thing: what NestJS does in one decorator, in Go takes 50 lines

When does it make sense to take on that cost? When the performance problem is real and documented, not hypothetical. The rule is simple: measure first, optimize later. If NestJS is surviving well and the business is growing, there is no urgency. If logs start showing P2024, timeouts, and p99 degradation under load, that's where the data from this benchmark becomes useful.


⚖️ Decision Matrix

image


🏆 Conclusion

The data points to a hybrid model as the most pragmatic architecture:

  • The high-traffic, concurrency, and serialization layer: Go

  • The business logic, domain rules, integrations: NestJS

  • Connect them via gRPC, and let each do what it does best

But if there's one thing I take away from this benchmark, it's that the key is not which technology is better, but understanding when the cost of adopting a new technology is justified by the problem you are solving. Go is not a magic tool. NestJS doesn't have as low a ceiling as sometimes claimed.

The data is there. The decision always depends on context.


💻 Source Code

I have published all the code (NestJS, Go, Next.js Dashboard, and Docker Compose) so you can run the benchmark on your own machine.

🔗 GITHUB LINK


💌 Special Acknowledgments

To a girl who, without knowing it, pushed me to keep creating and programming in my free time. To the one who was once my spark: for "YLP", with gratitude... and with scars that also teach.


If you find this content useful or it helps you make better technical decisions, I appreciate it. ☕️

Enjoy this post?

Buy Tomás Alegre a coffee

More from Tomás Alegre

PrivacyTermsReport