I Use FastAPI Daily. Here's When I'd Walk Away From It.

I've shipped production services in Python, TypeScript, C#, Java, and Elixir. FastAPI is what I reach for first. This post is about the situations where I wouldn't.
This isn't a performance horse race. It's an honest account of the moments in my career where a different language was clearly the right call, and a framework for recognizing when you're approaching one of those moments yourself.
First: Why FastAPI at All?
FastAPI's strengths are real and worth stating plainly before I critique it.
Async-native request handling, automatic OpenAPI documentation, Pydantic validation baked in, dependency injection that actually works. It's a remarkably productive framework for building APIs. Among Python frameworks, FastAPI has overtaken Django and Flask in recent surveys, now cited at around 38-40% adoption among Python web developers, driven by its async capabilities, type safety, and alignment with AI/ML and microservices workloads.
That growth is deserved. FastAPI is genuinely good at what it does.
But here's the thing I've learned from shipping it in production: FastAPI is not the architecture.
That phrase is the most important thing in this post. FastAPI makes it easy to build an API quickly. It does not make it easy to build a system. As an application grows, routers, services, schemas, background work, error handling, dependency injection patterns, structured logging, and testing strategy all need to be designed deliberately. FastAPI's flexibility becomes a liability if your team hasn't established discipline around each of those layers.
The framework won't save you from yourself. That's not a criticism; it's a characteristic. But it means that when I evaluate whether FastAPI is right for a project, I'm not just asking "will it handle the load?" I'm asking "does this team have the architectural discipline to not let it turn into a mess?"
Sometimes the answer is no. And sometimes the problem itself demands something different regardless.
The Performance Reality
Let's establish something clearly before getting into the walk-away scenarios: Python is the slowest language discussed in this post, and it's not close.
TechEmpower's Framework Benchmarks (Round 23, February 2025, the final round before the project was archived) are the most rigorous apples-to-apples comparison available. The Fortunes test is the most realistic scenario: it exercises database connectivity, dynamic collections, sorting, templating, and character encoding simultaneously.
| Framework | Language | Fortunes RPS | Relative Performance |
|---|---|---|---|
| ASP.NET Core | C# | 609,966 | ~36x |
| Spring Boot | Java | 243,639 | ~14x |
| Express | Node/TypeScript | 78,136 | ~5x |
| FastAPI | Python | ~20,000 | ~1.2x |
| Django | Python | 32,651 | ~2x (baseline) |
Source: TechEmpower Round 23 via dev.to/tuananhpham. FastAPI and Phoenix Elixir were not directly in this filtered comparison; FastAPI sits above Django among Python frameworks, and Phoenix is broadly comparable to Spring Boot in concurrent throughput.
ASP.NET Core's lead isn't a rounding error. It's dominant. TypeScript via Node.js sits in a meaningful middle ground, roughly 5x Python. Java lands at 14x. C# at 36x.
So why use FastAPI at all? Two reasons that actually hold up in production:
First, for I/O-bound workloads, which describes the majority of API services, your bottleneck is the database, not the framework. FastAPI with asyncpg and connection pooling is genuinely competitive for those workloads because the work is waiting on I/O, not executing Python.
Second, Python's ecosystem for ML/AI, data pipelines, and scientific computing is unmatched. If those capabilities are part of your surface area, no amount of throughput advantage in C# compensates for what you'd lose in libraries and tooling.
But those are specific, bounded reasons. Outside them, the performance gap is real and it compounds at scale. The rest of this post is about where that gap, combined with other factors, makes the walk-away decision clear.
When I'd Choose TypeScript Instead
The trigger: your team already lives in TypeScript, and the workload exposes Python's performance limits.
This is the most common walk-away scenario for teams building full-stack products. If your frontend is React and TypeScript, your team already thinks in types, async patterns, and JavaScript idioms. Crossing into Python for the API layer means context switching, split tooling, divergent type systems, and a hiring profile that requires developers to be fluent in two ecosystems simultaneously.
That friction is real and it compounds over time. Shared types between frontend and backend, unified linting and formatting pipelines, a single language for code review. These aren't luxuries; they're compounding productivity advantages.
The performance argument reinforces this. TypeScript via Node.js handles I/O-bound concurrency well through its event loop, and for CPU-bound work it reaches for Worker Threads rather than fighting Python's GIL. The ~5x throughput advantage over FastAPI in benchmarks isn't decisive for most CRUD workloads, but it matters when you're handling high-concurrency API services, WebSocket connections at scale, or services where memory footprint under load is a constraint.
Python's GIL is worth being explicit about: it prevents true parallelism for CPU-bound work within a single process. FastAPI works around this well for async I/O, which genuinely isn't a problem for most API patterns. But the moment your service needs to do real computation alongside request handling, you're either spinning up worker processes, offloading to a queue, or fighting the runtime.
TypeScript doesn't have that problem. It's not compiled, but it's not GIL-constrained either.
Switch to TypeScript when the team cohesion argument and the performance argument point in the same direction. Either one alone might not be enough. Both together usually is.
When I'd Choose C# Instead
The trigger: you're entering an enterprise environment, and .NET is already there.
Most of my C# experience came from enterprise work in finance, healthcare, government, and insurance. These environments didn't ask me which language I preferred. They had existing .NET infrastructure, internal tooling built on ASP.NET, SQL Server in production, and teams who'd been writing C# for years. The decision was already made before I arrived.
C# worked well in those settings not because it beat FastAPI on a particular dimension, but because it was mature, strongly typed, and already embedded in the organization's operating model. Switching to Python would have been a migration project, not an improvement.
The short version: C# fit the enterprise environments I was in. Later, the architecture shifted toward cloud-native APIs, serverless workflows, and smaller services, so the tools shifted too.
The performance argument for C# is the strongest of any language in this post. ASP.NET Core at ~610k RPS in the Fortunes benchmark isn't just fast. It's in a different tier entirely. For services where throughput is a hard requirement and you're willing to invest in the .NET ecosystem, that gap is decisive.
What it costs you: a smaller open-source ecosystem outside the Microsoft world, a steeper onboarding curve for developers coming from Python or JavaScript backgrounds, and less natural fit for ML/AI workloads where Python's ecosystem is unmatched.
Don't switch to C# to escape FastAPI's limitations. Switch because the environment, team, or performance requirements genuinely call for it.
When I'd Choose Java Instead
The trigger: the domain has already chosen Java for you.
At Veloce Energy, I led a team making contributions to OpenEMS, an open-source energy management platform built in Java. There was no language decision to make. If you're building on top of a Java framework, extending a Java codebase, or integrating deeply with a Java ecosystem, you're writing Java. That's not a failure of judgment. That's how mature ecosystems work.
Beyond domain lock-in, the other legitimate Java trigger is enterprise team familiarity. Java has decades of enterprise adoption in finance, telecom, and large-scale SaaS. When a team of senior engineers already knows Spring Boot deeply, rewriting in FastAPI to satisfy a language preference is a lateral move with real switching costs and no guaranteed payoff.
Spring Boot's ~14x throughput advantage over Python is real, but it comes with costs: JVM startup time that makes serverless deployments painful without SnapStart, a verbosity that slows iteration, and an ecosystem size that can be overwhelming rather than helpful if you're new to it.
The honest Java story: it's almost never the right greenfield choice for a small team building cloud-native APIs. It's frequently the right choice when you don't have a choice.
When I'd Choose Elixir Instead
The trigger: concurrency and fault tolerance are core to what you're building, not bolted on around it.
This is the most nuanced walk-away scenario, because Python can solve most concurrency problems. It's worth being precise about what Elixir actually offers, because the common framing, "Python can't handle concurrency," is both inaccurate and junior.
Here's the accurate version: Python can solve it. Elixir makes certain concurrency and fault-tolerance patterns simpler and more native.
With Python, I'd typically reach for asyncio, Celery for background tasks, Redis for queuing, and external orchestration depending on the workload. That works. It also spreads concurrency and failure handling across several tools, each with their own operational overhead, failure modes, and monitoring requirements.
Elixir's BEAM runtime takes a different approach. Concurrency, process isolation, message passing, and recovery aren't things you bolt on. They're part of the application model. A supervision tree in Elixir handles process crashes the way a load balancer handles server crashes: automatically, with defined restart strategies, without the rest of the system knowing something went wrong.
At Quorum Business Solutions, I worked on Landdox, a land management application where this mattered. SaaS workflows, real-time data interactions, and high-concurrency API services where the reliability guarantees needed to be intrinsic, not assembled from external pieces.
Phoenix consistently ranks as the most admired and loved web framework in developer surveys since 2023-2025, praised for its developer joy, performance, and real-world reliability. It powers high-profile systems at companies like Discord for real-time features, Bleacher Report, and Pinterest.
The cost of Elixir is real: a small talent pool, a steeper functional programming learning curve, and an ecosystem that's excellent but narrow compared to Python or JavaScript. You're making a focused bet.
Switch to Elixir when you find yourself assembling a concurrency stack from five different tools. That's the signal the problem might be better served by a runtime that treats concurrency as a first-class concern.
The Decision Framework
Here's how I actually think about it:
Stay with FastAPI when: - Your team has established (or can establish) disciplined architectural patterns around it - The workload is primarily I/O-bound: database calls, external APIs, async pipelines - ML/AI integration is part of the picture, because Python's ecosystem is unmatched here - Iteration speed and hiring pool matter more than raw throughput - Your team knows Python and the switching cost isn't justified by a clear technical requirement
Consider TypeScript (Node.js) when: - Your team is already TypeScript-native, particularly on a shared full-stack codebase - The GIL or memory footprint is creating real constraints, not hypothetical ones - You need CPU-bound work handled without spinning up separate worker processes - Team cohesion and shared tooling outweigh the Python ecosystem advantages
Consider C# (ASP.NET Core) when: - You're operating in a .NET enterprise environment - Raw throughput is a hard requirement and the team has .NET expertise - Deep Azure integration is a priority
Consider Java (Spring Boot) when: - The domain ecosystem is Java: you're extending, not replacing - Your team has deep Spring expertise and rewriting has no clear upside - You're in a large enterprise organization where Java is the standard
Consider Elixir (Phoenix) when: - Concurrency and fault tolerance are core requirements, not edge cases - You're building real-time systems, long-running processes, or high-connection-count services - You're willing to invest in the talent and learning curve for a runtime that makes reliability native
The Honest Summary
The language decision matters less than most developers think, and more than most managers think.
FastAPI is genuinely good. I use it daily and I'd choose it again for the right problem. But Python is the slowest language in this comparison, and pretending otherwise doesn't serve you. The right question isn't "is FastAPI fast enough in a benchmark?" It's "does Python's performance profile fit the system I'm building, and does my team have the discipline to build a real architecture around it?"
When the answer to both is yes, FastAPI is hard to beat for productivity. When the answer to either is no, this post has given you four honest alternatives, each grounded in the specific scenarios where they actually win.
Don't force FastAPI into everything. And don't walk away from it without a real reason.
This is the second post in a series on backend language decisions. The first, Go, TypeScript, and Rust compared for backend services, covers performance benchmarks and decision frameworks for those three languages specifically.