Architecture · October 19, 2026 · Daniel Murillo
NestJS, Spring Boot, and FastAPI: does the language matter that much in 2026?
Three different stacks, similar ideas. What you can and cannot conclude from syntax, architecture, and public benchmarks.
The best architecture is not the one that wins an isolated benchmark. It is the one that solves the product’s needs, with the available team, within a sustainable operating budget.
Summary
NestJS, Spring Boot, and FastAPI belong to different ecosystems, but they share many ideas: separation of responsibilities, reusable components, dependency injection, validation, middleware, controllers, automated tests, and a structure that lets you organize large applications.
Their syntax and conventions are not identical, but today it is possible to build very similar architectures with any of the three. The decision should not be reduced to “Java is faster,” “Python is easier,” or “Node scales better.” What matters is the type of load, latency, CPU and memory use, data access, integrations, team experience, security, operations, and time to delivery.
AI assistance also reduces part of the cost of learning a new syntax, generating repetitive code, and transferring patterns between languages. It does not, however, eliminate the cost of debugging, validating, maintaining, operating, or understanding a system.
This article compares the three options from two perspectives: how applications are built and maintained, and what we can—and cannot—conclude from public benchmarks.
1. We are not comparing exactly the same thing
Before comparing, three layers must be distinguished:
- Language and runtime: TypeScript/JavaScript on Node.js, Java on the JVM, and Python on its runtime.
- Framework and ecosystem: NestJS, Spring Boot, and FastAPI, along with their containers, validators, servers, libraries, and tools.
- Deployed application: business logic, SQL queries, ORM, cache, external calls, queues, serialization, configuration, containers, and infrastructure topology.
A benchmark that measures a text response without a database does not answer which framework is better for an API that validates hundreds of fields, runs several queries, publishes events, and calls an external provider.
It is also not a fair comparison to measure FastAPI with direct SQL access against Spring using a full ORM, or to compare an optimized application with another configured conservatively.
2. Philosophies and structure
| Topic | NestJS | Spring Boot | FastAPI |
|---|---|---|---|
| Usual language | TypeScript | Java | Python |
| Runtime | Node.js | JVM | Python runtime, normally with ASGI |
| Focus | Structured framework for Node.js applications | Broad, configurable platform for enterprise applications | API framework with typing, validation, and automatic documentation |
| Organization | Modules, controllers, providers, and services | Beans, components, configuration, and application modules | Routers, dependencies, models, and Python modules |
| Dependency injection | Nest’s own container | Spring’s IoC container | Depends system; not a container equivalent in scope to Nest or Spring |
| Routing | Decorators on controllers | Annotations on controllers or functional routes | Decorators on functions |
| Validation | Frequently class-validator and class-transformer | Jakarta Bean Validation and/or custom validation | Pydantic |
| Middleware / interceptors | Middleware, guards, interceptors, and pipes | Filters, interceptors, servlet filters, and other mechanisms | ASGI middleware and dependencies |
| OpenAPI documentation | Common integration with @nestjs/swagger | Springdoc and other integrations | Generated from types and models |
| Testing | Jest by default in many projects; integration with Node tools | JUnit, Spring Test, and JVM ecosystem | pytest and TestClient/HTTPX |
| Common use | Structured TypeScript APIs, team applications, and Node services | Enterprise systems, complex domains, and JVM integrations | APIs, data services, and applications close to the Python/ML ecosystem |
The table describes common conventions, not rigid limitations. All three allow different architecture styles, and each ecosystem offers alternatives.
3. Decorators and annotations: similar syntax, different mechanisms
One of the most visible similarities is that the code declares its intent near the class or function that implements it.
NestJS
import { Controller, Get, Injectable, Module } from '@nestjs/common';
@Injectable()
export class UsersService {
findAll() {
return [{ id: 1, name: 'Ada' }];
}
}
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Get()
findAll() {
return this.usersService.findAll();
}
}
@Module({
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}
Spring Boot
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@Service
class UsersService {
List<String> findAll() {
return List.of("Ada");
}
}
@RestController
class UsersController {
private final UsersService usersService;
UsersController(UsersService usersService) {
this.usersService = usersService;
}
@GetMapping("/users")
List<String> findAll() {
return usersService.findAll();
}
}
Spring detects components and resolves the dependency through its container, as long as the application is configured to scan or register them. You do not need every class to have a @Module annotation as in Nest.
FastAPI
from fastapi import APIRouter, FastAPI, Depends
router = APIRouter(prefix="/users")
class UsersService:
def find_all(self):
return [{"id": 1, "name": "Ada"}]
def get_users_service():
return UsersService()
@router.get("/")
def find_all(service: UsersService = Depends(get_users_service)):
return service.find_all()
app = FastAPI()
app.include_router(router)
FastAPI uses decorators to declare routes and a dependency system to resolve components. Depends is useful for sharing logic, authentication, sessions, or services, but it does not automatically equate to the lifecycle and scope of Nest’s or Spring’s container.
Are decorators and annotations the same?
Not exactly.
- In TypeScript, Nest’s decorators rely on the language’s decorator functionality and on metadata that the framework interprets.
- In Java, annotations are metadata that Spring processes through scanning, configuration, and its container mechanisms.
- In Python, decorators are functions that receive and modify or wrap other functions or classes. FastAPI uses them to register routes and extract information.
The conceptual result is similar: declare behavior without manually writing all registration and wiring. The implementation and language rules are different.
Conclusion: knowing the pattern in one framework helps you learn the others. It does not mean the code can be transferred line by line or that their internal mechanisms are identical.
4. Modularity, layers, and domain boundaries
All three allow separating an application into pieces with clear responsibilities.
A reference structure for an API could be:
src/
users/
users.routes-or-controller
users.service
users.repository
users.dto-or-schema
orders/
orders.routes-or-controller
orders.service
orders.repository
orders.dto-or-schema
shared/
auth/
observability/
database/
The real tree depends on the framework. What matters is that the structure does not turn every module into a collection of files that call each other without clear boundaries.
NestJS
Modules group controllers and providers. Provider visibility can be controlled through exports and imports. This helps delimit capabilities, although it does not by itself guarantee a clean architecture: you can still create circular dependencies or overly coupled modules.
Spring Boot
Modularity can be expressed through packages, components, configuration, build modules, and tools such as Spring Modulith. The ecosystem ranges from simple applications to very large enterprise domains. Flexibility also requires agreeing on conventions to avoid design depending on inconsistent decisions.
FastAPI
APIRouter instances allow splitting routes by domain or feature. Separating services, repositories, and models usually relies on Python modules and project conventions. FastAPI does not enforce a single structure; that freedom is useful for starting quickly, but teams must define boundaries and dependencies as the system grows.
Key idea: modularity is not creating many folders. It is being able to change one part of the system without breaking the others, test it in isolation, and understand who is responsible for each business rule.
5. Dependency injection and composition
| Need | NestJS | Spring Boot | FastAPI |
|---|---|---|---|
| Inject a service | Constructor with registered provider | Constructor with managed bean | Parameter with Depends or explicit composition |
| Replace a dependency in tests | Testing providers and overrides | Test configuration, mocks, and test beans | Dependency overrides and mocks |
| Share authentication or context | Guards, providers, and decorators | Security filters, beans, and other components | Dependencies and middleware |
| Control lifecycle | Provider scopes and Nest lifecycle | Bean scopes and Spring lifecycle | Depends on the pattern, yield in dependencies, and resource management |
Dependency injection reduces coupling when used with intent. You do not need to introduce a complex container for every project: a function, a constructor, and a clear interface can be enough in small systems.
A practical rule: use the simplest composition tool that keeps the system testable and understandable.
6. Validation, errors, and API contracts
All three ecosystems offer ways to validate inputs, although the mental model and configuration change.
- NestJS: DTOs and pipes, often with
class-validatorandclass-transformer. It is important to configure transformation and the unknown-field policy correctly. - Spring Boot: input objects, Jakarta Bean Validation, and centralized error handling, for example via
@ControllerAdvice. - FastAPI: Pydantic models, type annotations, and OpenAPI schema generation.
Validation does not end at checking that a field is a string or a number. You must validate business rules, authorization, limits, states, and consistency. A request can be syntactically valid and still not be allowed.
It is also worth standardizing errors: correct HTTP codes, useful messages without leaking secrets, correlation identifiers, and a stable structure for the client.
7. Asynchrony and concurrency: beware of oversimplifications
It is not accurate to say that “Node is asynchronous, Java is multithreaded, and Python is slow.” That phrase oversimplifies and leads to bad decisions.
Node.js and NestJS
Node.js uses an event loop and asynchronous APIs for many I/O operations. This works well with many connections that spend a good part of their time waiting on databases, networks, or files. A CPU-intensive task run synchronously can block the event loop; for those cases you should consider workers, processes, or specialized services.
NestJS can work with Express or Fastify. Changing the HTTP adapter can reduce server overhead, but it does not eliminate the cost of validations, serialization, ORM, queries, or business logic.
Java and Spring Boot
The JVM has JIT compilation, mature memory management, and multiple concurrency models. Spring MVC usually uses a servlet model; Spring WebFlux offers a reactive model, and virtual threads can be an alternative on compatible versions of Java and Spring. Not every application needs reactive programming: complexity and performance must be evaluated in the context of the workload.
Python and FastAPI
FastAPI relies on ASGI and Starlette, and normally runs with a server such as Uvicorn. async def is useful when the I/O operations called are truly asynchronous. A blocking function inside an asynchronous route can reduce responsiveness. For CPU-intensive work it is usually necessary to evaluate processes, workers, or running that load outside the HTTP server.
The important question is not which language has an async keyword, but what happens while each request waits, how much CPU work it does, and which resources remain occupied.
8. Public benchmarks: what the data say
8.1 TechEmpower Round 23
TechEmpower published round 23 on February 24, 2025. Its suite includes plaintext, JSON serialization, database queries, updates, and more realistic response generation. The official repository was archived in March 2026, so this round should be cited as an important public reference, not as a measurement freshly run in October 2026.
In the Fortunes test of that round, the published results include:
| Implementation | Requests/s | Test context |
|---|---|---|
| Spring (Servlet/Undertow, PostgreSQL, micro ORM) | 243 639 | Spring application of the indicated configuration |
| NestJS with Fastify, PostgreSQL, and full ORM | 160 502 | NestJS-Fastify implementation |
| FastAPI with Gunicorn, PostgreSQL, and raw access | 109 166 | FastAPI implementation with raw data access |
| NestJS with default Express, PostgreSQL, and full ORM | 82 444 | NestJS implementation |
| FastAPI with Hypercorn and PostgreSQL, raw access | 11 543 | Another configuration; anomalous result that requires caution |
Source: TechEmpower Framework Benchmarks, Round 23.
Fundamental warning: these rows do not constitute a perfectly controlled comparison. Configurations differ in server, ORM or data access, and possibly other implementation details. In particular, the FastAPI with Hypercorn result is very different from FastAPI with Gunicorn and should not be interpreted as a general property of FastAPI. The results serve to generate hypotheses, not to claim that one framework will always be a certain percentage faster than another.
FastAPI’s documentation also warns that you must compare implementations that do the same work: the ASGI server, Starlette, and FastAPI are not equivalent layers, and adding validation and features has a cost. Source: FastAPI benchmarks documentation.
8.2 NestJS with Express versus NestJS with Fastify
NestJS’s official documentation notes that Fastify can be an alternative when HTTP speed is a priority and that, in its reference benchmarks, Fastify can approach doubling Express’s results. The documentation itself clarifies that Nest keeps Express as the default adapter for its broad middleware compatibility.
Source: NestJS: Performance (Fastify).
TechEmpower round 23 also shows a difference between the published NestJS with Fastify and NestJS with Express implementations: 160 502 versus 82 444 responses per second in the Fortunes test. That equals approximately 1.95× on those two concrete entries. It is not a universal guarantee or a perfectly isolated experiment: the exact configurations must be verified before attributing the entire difference to the adapter.
Fastify also publishes its own benchmark suite, with explicit warnings about virtual hardware noise and the unrealistic nature of some tests. On its page updated October 12, 2026, Fastify 5.12.5 reports 89 258 requests per second and 10.68 ms latency in its illustrative comparison with other Node.js frameworks. The figure corresponds to Fastify’s suite, not to a full NestJS application.
Sources:
Partial conclusion: if NestJS was already chosen, trying the Fastify adapter is a reasonable optimization when the load profile justifies it. Before migrating, check compatibility of plugins, middleware, uploaded files, WebSockets, and other integrations that depend on the adapter.
8.3 Benchmarks of “normal” projects and automated tests
There is no single public benchmark that represents all business applications. A real API may be limited by PostgreSQL, by external HTTP calls, by serialization, by authorization logic, or by a provider that responds slowly.
A reproducible community project compares FastAPI, Node.js/Express, and Spring Boot with the same endpoint and query, load generated with k6, and response and memory metrics visualized in Grafana. It is a useful reference for learning how to design an equivalent test, but its results should be treated as results of that project and its configuration, not as a definitive framework ranking.
Source: fastapi-nodejs-springboot-benchmark.
For a rigorous technical article, I would separate benchmarks into three groups:
- HTTP microbenchmarks: minimal endpoint, small serialization, no database.
- API benchmarks with data: validation, authentication, queries, ORM, and representative responses.
- System benchmarks: deployed application with CPU and memory limits, database, cache, queues, observability, and external dependencies.
I would not mix the results of those groups into a single ranking.
9. How I would design my own reproducible comparison
If the goal is to publish a defensible conclusion, I would build the same small system in all three frameworks.
Same functional contract
GET /health: check availability.GET /products/{id}: retrieve a record.GET /products: pagination and filters.POST /orders: validation, transaction, and order creation.GET /orders/{id}: retrieve an order and its details.- An endpoint that calls a simulated external dependency, to measure I/O waits.
Keep conditions constant
- Same hardware, operating system, and container limits.
- Pinned versions of runtime, framework, ORM, driver, and dependencies.
- Same database, schema, indexes, data volume, and connection pool.
- Same functional logic, validation rules, and serialization shape.
- Same load generator and scenario.
- Warm-up before measuring, several repetitions, and publication of configuration and code.
- Do not run the load generator in the same container or CPU as the service under test.
Minimum metrics
| Metric | Why it matters |
|---|---|
| Throughput (requests/s) | Capacity to process requests under a defined load |
| Latency p50, p95, and p99 | Usual experience and queues of slow requests |
| Error rate | Avoids rewarding fast systems that fail under pressure |
| CPU | Compute cost for a service level |
| Steady and peak memory | Footprint and risk of memory pressure |
| Startup time | Important in serverless, autoscaling, and frequent deployments |
| Build and image time | Impacts CI/CD and deployment |
| Cost per work volume | Connects performance with real operating cost |
I would not report only the maximum requests per second. It is important to indicate concurrency level, duration, hardware, version, response size, and whether the database is inside or outside the measured environment.
A useful scenario matrix
| Scenario | What it lets you observe |
|---|---|
| Minimal endpoint | Framework and server overhead |
| JSON and validation | Parsing, validation, and serialization cost |
| Database read | Driver, pool, ORM, indexes, and SQL latency |
| Transactional write | Contention, transactions, connections, and consistency |
| Slow external I/O | Concurrency and wait management |
| CPU-intensive | Runtime behavior and execution strategy |
| Sustained load | Memory, degradation, stability, and limits |
| Cold start | Time until ready to receive traffic |
The test should be representative enough to guide a decision, but small enough for others to reproduce it.
10. Beyond speed: cost of building and operating
| Criterion | NestJS | Spring Boot | FastAPI |
|---|---|---|---|
| Productivity with a TypeScript team | Very high if the team already masters the ecosystem | May require changing stacks | May require changing stacks |
| Conventions for large teams | High; explicit structure | High; mature enterprise ecosystem and conventions | Flexible; the team must agree on more conventions |
| Enterprise business ecosystem | Broad, with strong Node/TypeScript affinity | Very broad, especially in enterprise Java | Broad and especially attractive for data/AI |
| Integration with Python data/ML tools | Possible via services or bindings, but not native | Possible via services or bindings, but not native | Natural by proximity to the Python ecosystem |
| Typing and contracts | TypeScript, plus explicit runtime validation | Java static typing and ecosystem validation | Type annotations and Pydantic at runtime |
| Concurrency options | Event loop, workers, and processes depending on the case | Threads, virtual threads on compatible stacks, and WebFlux | ASGI, asyncio, workers, and processes depending on the case |
| Complexity risk | Dependencies and decorators can hide flow if overused | Many abstractions and auto-configuration can raise the learning curve | Structural freedom can produce inconsistencies without conventions |
| Operations considerations | Node runtime and ecosystem dependencies | JVM, memory configuration, and observability ecosystem | ASGI server, workers, and Python dependencies |
| Strong selection criterion | Reuse TypeScript and deliver structured APIs | Leverage JVM experience and enterprise capabilities | Integrate API with Python, data, or AI models |
These are general trends, not absolute verdicts. A team can have excellent practices with FastAPI and poor practices with Spring, or the reverse.
Total cost of ownership (TCO)
Cost is not only the server price. A reasonable evaluation considers:
- Hours of initial development and later changes.
- Ease of hiring, training, and retaining the team.
- CI, build, test, and deployment time.
- CPU and memory for the required service level.
- Observability, incidents, maintenance, and upgrades.
- Integration complexity and library dependency.
- Time the team spends understanding code generated or modified with AI.
If a solution processes twice as many requests per second, but real load uses only a fraction of that capacity, the economic benefit may be irrelevant. If the application is at the CPU limit or compute cost is high, the difference can be decisive.
11. AI changes the equation, but does not remove it
Coding assistants help translate patterns, create controllers, write tests, generate DTOs, propose queries, and explain unfamiliar APIs. That is why learning a new framework no longer necessarily means starting from scratch.
But AI does not make all languages equal or make team experience irrelevant. You still must review:
- Whether the generated code respects real versions and APIs.
- Whether the chosen dependencies and patterns remain maintainable.
- Whether concurrency and resource management are correct.
- Whether tests cover business rules and failures.
- Whether vulnerabilities, inefficient queries, or authorization errors were introduced.
- Whether the team understands the system well enough to debug it at three in the morning.
AI’s advantage is reducing the cost of producing and exploring options; the responsibility to validate the design remains with the team.
That is why a technology decision can prioritize existing experience, hiring ease, integrations, and maintainability without ignoring performance. Performance should be measured when it represents a significant risk or cost, not turned into an abstract competition.
12. So which would I choose?
I would choose NestJS when…
- The team already works with TypeScript and Node.js.
- An explicit structure of modules, providers, guards, pipes, and interceptors is valued.
- Sharing knowledge and types with a TypeScript frontend is desired.
- Workloads are mainly I/O and tests show that performance is sufficient.
- The team wants flexibility to choose the HTTP adapter and the rest of the ecosystem.
I would choose Spring Boot when…
- The team has solid experience in Java and the JVM.
- The domain has many rules, integrations, and enterprise needs.
- Mature security, transactions, metrics, health checks, and observability capabilities are important.
- The organization already has JVM libraries, practices, infrastructure, and experience.
- Tests and operations requirements justify the resource profile and platform complexity.
I would choose FastAPI when…
- The application is closely connected with Python, data, automation, or AI models.
- Developing APIs quickly with types, models, and automatic documentation is valued.
- The team knows Python well and is willing to define structural conventions to grow.
- I/O dependencies are compatible with the chosen execution model.
- Performance is validated with the real stack, including ASGI server, workers, driver, and database.
None of these lists implies exclusivity. All three can build productive, scalable APIs.
13. A suitable architecture depends on context
The choice should start with concrete questions:
- What does the system need to do and which parts are critical?
- What load do we expect: requests, concurrency, payload size, and latency?
- Where does the time go: CPU, SQL, network, serialization, or external dependencies?
- What can the current team maintain?
- What integrations and tools already exist?
- What are the security, observability, deployment, and availability requirements?
- What budget and operating cost can be assumed?
- How easy will it be to change the system in one or three years?
Then build a minimal solution, measure, and decide. If the bottleneck is a query without an index, changing languages does not fix the problem. If the event loop is blocked by a CPU task, change the execution strategy. If the JVM or container configuration consumes too much memory for a small service, measure and optimize that footprint. If the application needs to integrate Python libraries, proximity to that ecosystem can save more time than any RPS difference.
Architecture can also be hybrid when there is a real reason: for example, a main API in NestJS and a specialized Python service for a data task. But splitting into services adds deployments, network, observability, security, and consistency; it is not worth doing only because several languages can be used.
14. Conclusion
NestJS, Spring Boot, and FastAPI are not identical, but they share enough concepts for architectural knowledge to transfer. Decorators and annotations, modules, dependencies, controllers, validation, and tests are different expressions of common problems.
Benchmarks are valuable, but their results depend on what is measured and how. Public NestJS with Fastify tests show that the HTTP adapter can have an important impact on a given test. Spring Boot and FastAPI results also show that an isolated ranking should not become a universal rule. A real API adds decisions about database, logic, concurrency, memory, infrastructure, and operations.
With AI help, experimenting with another language costs less than before. That makes it more important to know how to formulate the problem, design tests, and validate results. It does not mean experience stops mattering: it means we can dedicate more energy to the decisions that truly determine the system’s success.
I would not choose a language to win an internet argument. I would choose the stack that lets my team deliver the right product, operate it with confidence, and change it without fear. And if performance is a critical condition, I would measure it with the load that actually matters.
Related reading
- Tool: which backend fits you
- Authentication: sessions, JWT, and OAuth
- SQL, query builders, and ORMs
- Node.js vs Bun vs Deno
- Frontend architecture decision
- Benchmarks and architecture decisions
References
- TechEmpower, Web Framework Benchmarks — Round 23 (published February 24, 2025): https://www.techempower.com/benchmarks/
- NestJS, Performance (Fastify): https://docs.nestjs.com/techniques/performance
- Fastify, Benchmarks (data updated October 12, 2026): https://fastify.dev/benchmarks/
- Fastify, Benchmark repository: https://github.com/fastify/benchmarks
- FastAPI, Benchmarks: https://fastapi.tiangolo.com/benchmarks/
- Spring Boot, official documentation: https://docs.spring.io/spring-boot/
- Spring Boot, Testing: https://docs.spring.io/spring-boot/reference/testing/index.html
- Spring Boot, Observability: https://docs.spring.io/spring-boot/reference/actuator/observability.html
- Community reproducible comparison project: https://github.com/FalkAurel/fastapi-nodejs-springboot-benchmark
- TechEmpower, Round 23 closure announcement and repository archive: https://github.com/TechEmpower/FrameworkBenchmarks/issues/9589
Editorial note
Benchmark figures are snapshots tied to specific versions, hardware, and configurations. Before publishing an update, review the original pages and, if new numbers are added, record consultation date, exact versions, test type, and configuration. Do not extrapolate one test’s result to every project.