You've built a Next.js application and find yourself drowning in "use client" directives, questioning if you're really leveraging the framework's power. Or perhaps you're staring at TypeScript errors from tRPC that make you wonder if a simple REST API would have been less frustrating. You're not alone.
"I personally find it pretty hard compared to a simple REST API, getting errors and can't get it to work at first try," confesses one developer who attempted to learn tRPC. While modern TypeScript-first tools like tRPC offer compelling benefits, they're not always the right solution for every project.
This analysis explores when good old REST might be the better choice over tRPC, focusing on complexity, performance, and development workflow considerations.
Understanding REST and tRPC
What is REST?
REST (Representational State Transfer) is an architectural style for designing networked applications that relies on a stateless, client-server communication protocol, typically HTTP. REST APIs use standard HTTP methods (GET, POST, PUT, DELETE) to perform operations on resources, which are identified by URLs.
Key characteristics of REST include:
Client-Server Architecture: Separates client and server concerns, enhancing scalability
Stateless Communication: Each request contains all information needed to complete it
Cacheable Responses: Allows clients to cache responses, improving performance
Uniform Interface: Ensures a consistent method for resource manipulation
Resource-Based: Operations performed on resources through standard HTTP methods
What is tRPC?
tRPC is a TypeScript-based library that creates end-to-end type-safe APIs without schemas or code generation. It uses Remote Procedure Call (RPC) patterns to allow you to call server-side functions directly from the client with full type safety.
Key features of tRPC include:
End-to-End Type Safety: Eliminates the need for separate API type definitions
React Query Integration: Built-in support for data fetching with TanStack Query
Zero Generated Code: No code generation step required
TypeScript-First Design: Optimized for TypeScript development environments
When to Choose REST Over tRPC
1. When You Value Simplicity and Familiarity
If your development team is already familiar with REST principles and less experienced with TypeScript's advanced type system, REST can significantly reduce the learning curve.
"For me it was a pain to get setup," admits one developer about tRPC, "but once it was running it really sped up development." This initial friction shouldn't be underestimated, especially for teams under tight deadlines or with varying TypeScript expertise.
REST's widespread adoption means:
More available documentation and examples
Greater developer familiarity
Simpler onboarding for new team members
Less cognitive overhead when working with the API
Consider choosing REST when:
Your team has limited TypeScript experience
You need to minimize onboarding time for new developers
You want to leverage existing knowledge and patterns
2. When Your Architecture Demands Clear Separation of Concerns
One common concern with tRPC is its tendency to blur the lines between frontend and backend:
"If you're using RPC, you are already highly coupled. Adding types doesn't make it worse," points out one skeptical developer.
REST enforces a clearer separation between client and server, making it particularly valuable when:
Your Teams Are Specialized: Separate frontend and backend teams can work independently with clearly defined interfaces
You're Building Public APIs: Third-party developers need well-documented, stable endpoints
You Have Multiple Clients: Various applications or services consume your API, not just a single frontend
As one developer aptly put it, "When you have a hammer, not everything is a nail." tRPC excels in monolithic, full-stack TypeScript environments but can feel constraining when your architecture demands more separation.
3. When Integration With Existing Systems Is a Priority
REST's ubiquity makes it the de facto standard for system integration. If your application needs to interact with multiple external services or legacy systems, REST offers:
Broad Compatibility: Almost every programming language and framework supports HTTP requests
Simpler Authentication Integration: Well-established patterns for API keys, OAuth, and other auth mechanisms
Easier Gateway Configuration: API gateways and proxies are typically designed around REST conventions
Better Tooling Support: More mature ecosystem of testing and monitoring tools
In contrast, tRPC's TypeScript-first approach can create integration challenges with non-TypeScript systems or when working with API Gateway services that expect traditional REST endpoints.
4. When Performance Requirements Are Specific
While tRPC can be highly performant, REST gives you more control over specific performance optimizations:
Fine-Grained Caching Control: REST's resource-oriented approach works naturally with HTTP caching mechanisms
Optimized Payload Size: You can tailor each endpoint to return exactly what's needed, no more or less
Bandwidth Considerations: REST can be more efficient in bandwidth-constrained environments when properly designed
"I'm wondering if I should be fetching data on the server instead to improve performance?" questions a developer using tRPC. This highlights a common concern - sometimes the abstraction provided by tRPC can obscure the most efficient data-fetching strategy.
5. When You Need More Control Over Implementation Details
Some developers feel tRPC abstracts away too much: "It felt like it abstracted away too much and I like more fine control over everything."
REST provides greater control over:
Custom Middleware: Easily add custom request/response processing
Status Codes: Utilize the full range of HTTP status codes for precise error handling
Headers and Metadata: Take advantage of HTTP headers for caching, content negotiation, etc.
Request Validation: Implement custom validation logic at each layer
Response Formatting: Control exact structure of responses without framework constraints
This control becomes particularly valuable when building systems with complex security requirements, specific performance needs, or unique architectural constraints.
Real-World Scenarios Favoring REST
Legacy System Integration
When integrating with existing systems or third-party services that expect REST endpoints, maintaining consistency in your API design can prevent unnecessary adaptation layers. REST's universality makes it the lingua franca of web APIs.
Public API Development
For APIs intended for public consumption or third-party developers, REST provides a familiar, well-documented interface. The self-descriptive nature of well-designed REST APIs reduces the learning curve for consumers.
Cross-Functional Teams with Varied Expertise
In environments where frontend and backend development are handled by separate teams with different skill sets, REST provides a clear contract between services. As one developer noted, "If your workflow involves separate teams for frontend and backend, consider maintaining a clear separation of concerns rather than tightly coupling with tRPC."
Simple CRUD Applications
For straightforward CRUD operations where the added complexity of tRPC doesn't provide substantial benefits: "REST is much simpler and easy to implement for simple resource calls," explains a developer comparing different API approaches.
Projects with Non-TypeScript Backends
When your backend is built with Python, Java, Ruby, or any non-TypeScript language, REST becomes the natural choice, as tRPC's primary advantage of end-to-end type safety isn't applicable.
Practical Recommendations
If you're considering REST over tRPC, here are some best practices to maximize its effectiveness:
1. Combine REST with Schema Validation
While you lose automatic type safety with REST, tools like drizzle-zod
or other schema validation libraries can help maintain data integrity between your client and server:
// Server-side validation with Zod
import { z } from 'zod';
const UserSchema = z.object({
name: z.string().min(2),
email: z.string().email(),
role: z.enum(['admin', 'user'])
});
app.post('/api/users', (req, res) => {
const result = UserSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({ errors: result.error.errors });
}
// Process valid user data...
});
2. Consider Server Actions for Next.js Projects
Next.js Server Actions can provide a compelling alternative to tRPC when you want type safety without the complexity:
'use server'
import { z } from 'zod';
const FormSchema = z.object({
email: z.string().email()
});
export async function subscribe(formData: FormData) {
const email = formData.get('email');
const result = FormSchema.safeParse({ email });
if (!result.success) {
return { error: 'Invalid email address' };
}
// Process subscription...
return { success: true };
}
Libraries like next-safe-action
can enhance this approach with additional validation and safety.
3. Leverage Type Generation for REST APIs
Tools like OpenAPI can generate TypeScript types from your REST API definitions, bringing some of tRPC's type safety benefits:
// Using generated types from OpenAPI spec
import { User, CreateUserRequest } from './api-types';
async function createUser(userData: CreateUserRequest): Promise<User> {
const response = await fetch('/api/users', {
method: 'POST',
body: JSON.stringify(userData),
headers: { 'Content-Type': 'application/json' }
});
return response.json();
}
4. Use TanStack Query with REST
You can still get the benefits of TanStack Query's caching, refetching, and state management with REST APIs:
const { data, isLoading, error } = useQuery({
queryKey: ['users'],
queryFn: () => fetch('/api/users').then(res => res.json())
});
This approach gives you sophisticated data fetching capabilities without requiring tRPC's entire infrastructure.
Conclusion
While tRPC offers compelling benefits in TypeScript-centric environments—particularly in monorepos with full-stack TypeScript—REST remains a powerful, flexible choice for many scenarios. As one developer soberly reminds us, "REST is much simpler and easy to implement for simple resource calls."
The decision ultimately depends on your specific project needs:
Choose REST when architectural flexibility, team structure, or system integration requirements favor a more traditional, decoupled approach.
Consider tRPC when working in a TypeScript-first environment where the benefits of end-to-end type safety outweigh the initial learning curve and architectural constraints.
By understanding the strengths and limitations of each approach, you can make an informed decision that aligns with your team's expertise, project requirements, and long-term architectural goals. Sometimes, the mature, battle-tested approach of REST is exactly what your project needs to succeed.