Someone on your team has asked whether the new B2B SaaS API should be tRPC, REST or GraphQL, and the discussion has stalled because each option sounds right. They each fit a different caller. Your first action: list every kind of caller your API will have in the next twelve months (your own dashboard, a mobile app, partner systems, webhooks) and mark which ones you don't control. That list decides most of the answer.
Which API Style Should a B2B SaaS Use: tRPC, REST or GraphQL?
Use tRPC for callers that share your TypeScript code, REST for anything a partner or third party must call, and GraphQL only once one client needs data from several backends in variable shapes.

The real difference is who is allowed to call the API, not which one is technically superior. tRPC assumes the caller shares your types, REST assumes it doesn't, and GraphQL assumes the caller's data needs are unpredictable enough that a fixed response shape would be wasteful. Performance and tooling arguments mostly follow from that. The table maps each style to the dimensions that show up in production.
| Dimension | tRPC | REST | GraphQL |
|---|---|---|---|
| Type safety | Inferred from your TypeScript, no schema or codegen | Only as good as your OpenAPI spec and generated types | Typed schema; resolver bugs still happen |
| Who can call it | TypeScript clients that share your types | Any HTTP client in any language | Any client that can send a GraphQL request |
| Best fit | Authenticated dashboard and admin traffic | Partner APIs, webhooks, OAuth callbacks | Aggregating several backends for a variable client |
| Caching | Handled at your data or client layer | Resource URLs work with standard HTTP caches | GET for queries, persisted documents for long ones |
| Contract | Tied to your deploy | Documented, versioned resources | Versionless schema evolution |
Popularity data is only a rough signal, but it shows the scale gap. The npm registry's download counts for the week of 22 to 28 September 2026 show about 6.05 million downloads for @trpc/server and about 56.8 million for graphql. Those numbers include CI installs and transitive dependencies, so they don't prove what teams choose for their own APIs. They do suggest GraphQL is far more widely embedded across the ecosystem, while tRPC's audience is narrower and TypeScript-specific.
Is tRPC Only for Internal APIs?
Mostly yes: tRPC is built to keep client and server types in sync inside one TypeScript codebase, so it fits authenticated internal traffic and not partner-facing APIs.

The tRPC documentation describes it as a way to build and consume fully typesafe APIs without schemas or code generation, and frames the problem as keeping API contracts in sync between client and server in full-stack TypeScript projects. The npm registry currently lists version 11.19.0 as the latest release. Nothing in that description is about interoperability with other languages, and I couldn't find a non-TypeScript client story on the pages I checked, so treat that as unsupported unless you add another layer.
For a Next.js App Router dashboard, the tRPC App Router setup guide uses the fetch adapter in a route handler and shows server components prefetching queries and passing them to the client through a hydration boundary. That's a clean fit for anything behind login. My recommendation: don't treat tRPC as a stepping stone to a public API. If a partner asks for access from Python or a mobile team ships in Kotlin, you'll want a second surface for them, and building it from an OpenAPI contract written on purpose beats reverse-engineering procedures later.
When Does REST Still Win for Partner APIs?
REST wins wherever a partner, webhook sender or client outside your control needs a documented contract that doesn't assume shared code.

The OpenAPI Specification defines a standard, programming language-agnostic interface description for HTTP APIs, which is what lets a partner's engineer generate a client without asking you anything. Webhooks are the clearest example. Stripe's webhook documentation requires a publicly reachable HTTPS endpoint that accepts POST, verifies the Stripe-Signature header against the raw request body, and returns a 2xx quickly. Stripe also retries failed live-mode deliveries for up to three days and advises tracking event IDs to catch duplicates. That's an HTTP contract, and no type-sharing scheme could satisfy it.
BookBed shows the single-writer idea in a Flutter, Firebase and Stripe stack: its case study says webhook writes sync accountType in lockstep with subscription status, so nothing else has to guess billing state. In a Next.js and Postgres app, that writer would be a Route Handler. If you need the mechanics of surviving retries, webhook reliability, idempotency and dead-letter queues goes deeper, and what a REST API is at the wire level clears up the RPC-versus-REST confusion that keeps resurfacing in meetings. A REST surface also needs rate limits from day one, which rate limiting patterns for SaaS APIs covers.
What should a partner-facing REST surface include?
Start from what a partner's engineer needs on day one, and treat everything else as optional. Stripe's webhook documentation is a useful model because it documents both sides of the contract, and the pattern generalizes to your own API:
- An OpenAPI document that describes every endpoint, so a partner can generate a client and doesn't need to ask you questions.
- Authentication that's separate from your dashboard sessions, so revoking one partner doesn't log out your own users.
- Explicit rate limits and a documented response when a limit is hit.
- Signed webhooks with the signing scheme, the retry behaviour and the duplicate-handling advice written down, as Stripe does.
- A versioning rule you can honour, for example additive changes only inside a version.
Items 2 and 5 are my recommendations, not requirements from a source above. The point is that each item is something tRPC never asked you to write, which is exactly why partner traffic doesn't belong on it.
Is GraphQL Overkill for a B2B SaaS?
Usually, unless you aggregate data from more than one backend for clients whose needs vary; with one database and one app, plain REST endpoints cover the same ground with less machinery.
The GraphQL site describes it as enabling flexible, versionless APIs powered by a strong type system. Its HTTP guide is useful for judging the cost: servers operate on a single endpoint, usually /graphql; POST is required and GET is optional and only for queries; and long queries can exceed URL limits imposed by browsers and CDNs, which is why the guide points to persisted documents or automatic persisted queries. Those are extra pieces you now own. What GraphQL optimizes for is client-driven queries across heterogeneous sources. If your B2B SaaS has one database, one primary app and a few partner webhooks, you don't have that fan-out problem.
Consider a hypothetical team with an admin screen that needs data from four internal services at once. There, GraphQL as an aggregation layer earns its place quickly, because the alternative is four round trips or a bespoke backend-for-frontend. The mistake isn't choosing GraphQL. It's choosing it before you have more than one backend to aggregate.
One detail from the GraphQL HTTP guide affects monitoring. It says a response with a non-null data key can come back with a 2xx status even when it contains errors, while validation errors that prevent execution should return 400 when the server uses the application/graphql-response+json media type. Legacy servers using plain application/json may return 2xx for errors as well. In practice that means status-code dashboards alone can hide failing GraphQL operations, so you'll want to log the errors array. A REST endpoint's status codes carry more of that signal by default.
What if a partner insists on GraphQL?
Wrap your existing API in a thin GraphQL facade for that partner instead of rebuilding your data layer around one client's tooling. Your core stays boring and stable, and the partner gets the query shape they've already built around. If several partners ask for the same thing, treat that as a signal the public API deserves GraphQL as a first-class surface. The Next.js docs also note that you can keep calling existing REST or GraphQL endpoints from Server Components, so adopting the App Router doesn't force the choice either way.
The hybrid split I'd recommend
- tRPC for everything behind login: dashboard reads, internal mutations and admin actions.
- REST for anything outside your TypeScript boundary: Stripe webhooks, OAuth callbacks, health checks and mobile clients.
- A small, versioned REST subset for partners, generated from an OpenAPI contract you write deliberately.
- GraphQL only once you have a real aggregation problem.
Callidus is a useful counter-example to all three. Its case study describes a React front end talking to Firebase, with tenant data under tenants/{tenantId} in Firestore and access enforced by Firestore rules, plus Cloud Functions for scheduled work. Sometimes your database's rules layer is the boundary, and none of these API styles is the first decision. If you're still choosing the rest of the stack, the SaaS MVP stack guide and the Next.js and Prisma setup are where I'd check the pieces together, and what B2B SaaS means for API design frames the partner requirements.
Here's a question to answer this week: if you removed GraphQL from your stack and replaced it with two REST endpoints, what would you actually lose?
