DEV Community

Cover image for 🔬 NestJS vs Go: Performance Benchmark (Do you really need to migrate?)
Tomás Alegre Sepúlveda
Tomás Alegre Sepúlveda

Posted on

🔬 NestJS vs Go: Performance Benchmark (Do you really need to migrate?)

🇪🇸 Lee este post en Español

Hello everyone. I recently conducted an exhaustive benchmark to answer a classic question in backend teams: does it make sense to migrate to Go, or is NestJS enough?

There is no magic answer, but data helps decide. Here is a summary of the most important findings.


🏗️ The Scenario

I compared two typical architectures running in Docker on the same machine (M4 Pro):

  1. NestJS (Node 22) + Prisma v6 (Productivity approach)
  2. Go (1.24) + GORM (Performance approach)

Both connected to PostgreSQL 15 and subjected to stress tests (gRPC and HTTP) simulating a real environment.


🧪 Methodology: What did we measure?

To truly understand the differences, I divided the tests into two key scenarios:

  1. Small Data (1x1 Insert):
    Simulates typical transactional traffic of a REST/gRPC API. We receive an object, validate it, and save it. Here we measure the framework's base latency.

  2. Large Data (Batch Insert of 1000 elements):
    Simulates bulk load processes, ETLs, or event ingestion. We send arrays of 1000 objects that must be deserialized, validated, and saved in a single transaction. This is where CPU and Garbage Collector suffer the most.


📊 The Results

1. Small Data: The Surprise

In low load or unit insert scenarios, the difference was minimal. Go obtained a latency improvement of just ~10% over NestJS.

Conclusion: For standard CRUD operations, NestJS is incredibly efficient, and the performance difference rarely justifies losing TypeScript's productivity.

2. Large Data: The Breaking Point

The story changes drastically when processing massive batches (arrays of 1000 elements) under high concurrency.

Scenario: 100 RPS constant (Bulk Inserts)

Service Real Throughput P95 Latency Success Rate
Go gRPC 85 RPS 71.88 ms 100%
NestJS gRPC 78 RPS 98.44 ms ~85%

What happened here?

  1. Serialization: Node.js (V8) has a higher cost when serializing/deserializing large volumes of JSON compared to Go Structs and Protobuf.
  2. Connection Pool: Under extreme pressure, the Prisma connection pool started to saturate (P2024 errors), causing NestJS to lose ~15% of requests.
  3. Stability: Go maintained 100% success and more predictable latencies, thanks to its goroutine management and a more efficient GC for this type of load.

⚖️ Conclusion: When to use which?

Based on the data, here is my pragmatic recommendation:

✅ Stay with NestJS if:

  • You are building an MVP or validating a product.
  • Your team already knows TypeScript and you value development speed.
  • The load is moderate (< 300 RPS) and standard CRUD operations (Small Data).
  • BFF (Backend for Frontend): If you only need to orchestrate calls to a few services and the serialization load is low.

🚀 Migrate to Go if:

  • Complex BFF: If your BFF needs to call many microservices, add heavy logic, serialize large volumes of data, and perform massive fan-out, Go will handle concurrency much better.
  • Your system constantly performs massive Bulk Operations / ETL (Large Data).
  • You need to handle thousands of concurrent connections (e.g., Gateways, massive Websockets).
  • You have very strict latency SLAs where Node's Garbage Collector spikes are unacceptable.
  • Infrastructure is expensive and you need to squeeze every CPU cycle.

📖 Read the full analysis

This post is a summary. The detailed analysis (with all tables, charts, hardware configuration, and error analysis) is public and free.

👉 Read the full article on Buy Me a Coffee

💻 View code on GitHub


💌 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 analysis useful, any feedback is welcome!

Top comments (1)

Collapse
 
mickyarun profile image
arun rajkumar

Really like that you separated small-data from bulk — that's the distinction most "Node vs Go" benchmarks skip. The detail that jumps out at me: NestJS didn't fall over on raw throughput, it fell over when the Prisma pool saturated (the P2024s). That's a pool-sizing/tuning problem before it's a language problem — I'd want to see the same bulk test with a tuned pool + batched inserts before concluding the runtime is the ceiling. We run ~15 NestJS services in payments and stayed on Node for exactly your "small data, moderate RPS" reason; the day we'd reach for Go isn't a benchmark number, it's a sustained bulk/ETL workload where GC spikes start breaking a latency SLA — which is basically your "migrate if" list. Honest, well-structured writeup.