Node.js vs Go: Complete Benchmark with NestJS, Gin and PostgreSQL
I tested 800 simultaneous connections, database operations, CPU-bound tasks, and more. The numbers don't lie.
Every discussion about "which language is better" usually ends in opinions. I decided to put an end to the guesswork by creating two identical APIs, one in NestJS (Node.js) and one in Gin (Go), using PostgreSQL, and running real production benchmarks.
The Setup
- Hardware: MacBook Pro M1, 16GB RAM
- Database: PostgreSQL 16 (Docker)
- Tool: wrk (HTTP benchmark)
Both APIs implement the following requirements:
- CPU Endpoints (JSON, hash, fibonacci)
- Database Endpoints (CRUD, aggregation)
- Connection pool with 20 max connections
The Results
π Pure Performance (No Database)
| Test | NestJS | Gin | Go Advantage |
|---|---|---|---|
| Health Check | 16k req/s | 93k req/s | 5.8x |
| JSON (100 obj) | 4.3k req/s | 28k req/s | 6.4x |
| Fibonacci (n=30) | 96 req/s | 1.8k req/s | 19x |
| Bcrypt Hash | 64 req/s | 91 req/s | 1.4x |
Highlights:
- Go is 19x faster in pure CPU-bound processing.
- In bcrypt, the difference drops to 1.4x, as Node uses C bindings.
π₯ Stress Test (800 connections, 45 seconds)
| Metric | NestJS | Gin |
|---|---|---|
| Requests/sec | 16k | 90k |
| p99 Latency | 63ms | 15ms |
| Timeouts | 481 | 0 |
The most critical data point: NestJS exhibited 481 timeouts, while Gin had zero. In production, timeouts represent clients receiving direct errors.
ποΈ With PostgreSQL (200 connections)
| Operation | NestJS | Gin | Go Advantage |
|---|---|---|---|
| SELECT 1 row | 7.7k req/s | 18.7k req/s | 2.4x |
| SELECT 100 rows | 4.7k req/s | 9.3k req/s | 2.0x |
| Aggregation | 7.7k req/s | 13.4k req/s | 1.7x |
Insight: The difference dropped from a 5-19x scale to a range between 1.7x and 2.4x. This indicates that the database becomes the system's main bottleneck.
The p99 latency, however, remains very different:
- List 100 rows: NestJS 158ms vs Gin 33ms (a 4.7x lower latency).
πΎ Memory Usage
| Moment | NestJS | Gin |
|---|---|---|
| Startup | 68 MB | 11 MB |
| After stress | 85 MB | 52 MB |
Go consumes between 40% and 80% less memory than Node.js in similar scenarios.
What This Means in Practice
β Use Go (Gin) when:
- Performance is critical: APIs that need to sustain volumes exceeding 10k req/s.
- The domain is CPU-bound: intense data processing and transformation tasks.
- p99 latency is a priority: cases with very tight SLAs (Service Level Agreements).
- Resources are limited: Kubernetes environments with strict memory caps.
- Stability under load is necessary: situations where avoiding timeouts is essential.
β Use Node.js (NestJS) when:
- Development speed is the priority: leveraging features like TypeScript, Dependency Injection, and decorators.
- The team is focused on JavaScript: drastically reducing the learning curve.
- The domain is I/O-bound: situations where the database performance difference is acceptable for the business.
- Reliance on the NPM ecosystem: needing specific libraries already consolidated within the community.
Reproduce It Yourself
# Clone the repository
git clone https://github.com/zghost10/go-vs-node.git
# Start PostgreSQL
docker-compose up -d
# Start the APIs
cd nestjs-api && npm install && npm run build && node dist/main.js &
cd gin-api && go build && ./gin-api &
# Benchmark
wrk -t8 -c800 -d45s --latency http://localhost:3000/health
wrk -t8 -c800 -d45s --latency http://localhost:8080/health
wrk -t4 -c200 -d15s --latency http://localhost:3000/db/users/active
wrk -t4 -c200 -d15s --latency http://localhost:8080/db/users/active