APIArchitectureEngineering

API Architecture Decisions That Will Haunt You Later

February 20, 2026 · 8 min read

TL;DR
  • REST with JSON is the correct default for 90% of startups — GraphQL adds complexity you do not need yet
  • Authentication architecture is the hardest to change later — use a proven auth provider (Auth0, Clerk, Supabase Auth) from day one
  • Version your API from the first release (URL path versioning: /api/v1/) even if you think you will not need it
  • Rate limiting and pagination are not "later" features — add them before your first paying customer

API architecture decisions made in month 1 become constraints you live with for years. Some decisions are easy to change later. Others require migrating every client, rewriting authentication, or rebuilding your entire data layer. Here are the decisions that haunt startups — and how to make them well the first time.

Decision 1: REST vs. GraphQL

Choose REST When (Most Startups)

  • Your API serves a web frontend and possibly a mobile app that you control
  • Request patterns are predictable (standard CRUD operations)
  • Your team knows REST well
  • You want the simplest possible API layer

REST is boring, well-understood, and has massive tooling support. Every developer has built REST APIs. Every client library supports REST. Every monitoring tool tracks REST endpoints.

Choose GraphQL When

  • Multiple client types need significantly different data shapes from the same backend
  • You have a complex data model with deep relationships that clients need to traverse
  • Over-fetching is a measured performance problem (not a theoretical one)
  • Your team has GraphQL experience

The Trap

Many startups choose GraphQL because "it is modern" or "Facebook uses it." Then they discover:

  • Schema maintenance is ongoing work
  • N+1 query problems require DataLoader implementation
  • Caching is significantly more complex than REST
  • Error handling patterns are non-standard
  • Monitoring and debugging are harder
  • Every developer you hire needs GraphQL training

If you are a team of 2-5 developers building a SaaS product, REST with a well-designed resource structure will serve you for years. Switch to GraphQL only when REST is a proven bottleneck.

The Pragmatic Middle Ground

Start with REST. If specific endpoints suffer from over-fetching or under-fetching problems, consider adding a GraphQL layer for THOSE endpoints while keeping REST for everything else. You do not need to commit fully to one paradigm.

Decision 2: Authentication Architecture

Authentication is the single hardest API decision to change later. Every endpoint depends on it. Every client has auth logic embedded. Every user's session, token, and permission is tied to your auth implementation.

The Rule: Do Not Build Your Own Auth

Use an auth provider: Auth0, Clerk, Supabase Auth, Firebase Auth, or AWS Cognito. The reasons:

  • Auth providers have dedicated security teams patching vulnerabilities
  • They handle edge cases you have not thought of (token rotation, session hijacking, brute force protection)
  • They provide social login, MFA, and SSO out of the box
  • Migration from one auth provider to another is painful but possible. Migration from broken custom auth to anything is much worse.

If You Must Build Custom Auth

Sometimes regulatory requirements or deep integration needs require custom auth. In that case:

Non-negotiable requirements:

  • Passwords hashed with bcrypt or Argon2 (never SHA-256, never MD5, never plain text)
  • JWT tokens with short expiry (15-60 minutes) and refresh token rotation
  • HTTPS only (reject all HTTP requests)
  • Rate limiting on login endpoints (prevent brute force)
  • Account lockout after failed attempts
  • Secure password reset flow (time-limited tokens, single use)

Common mistakes:

  • Storing session state on the application server (breaks when you scale to multiple servers)
  • Long-lived tokens without refresh mechanism (security risk)
  • No token revocation capability (cannot log out compromised accounts)
  • Custom encryption instead of established libraries

Authorization (Permissions)

Separate authentication (who you are) from authorization (what you can do). Design your permission model early:

  • Simple (most MVPs): Role-based: admin, user, viewer
  • Medium: Resource-based: user can edit THEIR records, admin can edit ALL records
  • Complex: Attribute-based or policy-based (evaluate this later, not at MVP)

Start simple. Evolving from role-based to resource-based is straightforward. Starting with a complex permission model before you have users is premature.

Decision 3: API Versioning

Version your API from day one. The cost of adding versioning to an existing API is 10× the cost of including it from the start.

URL Path Versioning (Recommended)

GET /api/v1/users
GET /api/v2/users

Pros: Obvious, easy to route, easy to monitor, easy for clients to understand Cons: Feels heavy for early-stage ("we only have v1")

Header Versioning (Not Recommended for Startups)

GET /api/users
Accept: application/vnd.myapp.v1+json

Pros: Cleaner URLs Cons: Harder to test (requires header manipulation), less visible, clients often forget to set headers

When You Will Need Versioning

"We will never need to break backward compatibility" is something every team says before they need to break backward compatibility.

You will need versioning when:

  • A mobile app update is not instant (users on old versions need the old API)
  • Enterprise clients integrate with your API and cannot update immediately
  • You need to change a response format but existing clients depend on the current format
  • You discover a design mistake that requires restructuring an endpoint

URL path versioning costs almost nothing to implement from day one. Just prefix all routes with /api/v1/. When v2 is needed (and it will be), the path is clear.

Decision 4: Rate Limiting

Rate limiting is not a scale problem — it is a day-one security and stability requirement.

Without rate limiting:

  • A single misbehaving client can overwhelm your API
  • A DDoS attack has no defense
  • A bug in a client's retry logic can cascade into an outage
  • Automated scraping can access your entire dataset

Minimum rate limiting for an MVP:

  • Login endpoint: 5 attempts per minute per IP
  • API endpoints: 100 requests per minute per authenticated user
  • Public endpoints: 30 requests per minute per IP
  • Webhooks: 10 per second per destination

Implementation: Most API frameworks have rate limiting middleware. Express has express-rate-limit. NestJS has @nestjs/throttler. Use them.

Return proper HTTP 429 (Too Many Requests) with a Retry-After header so clients know when to retry.

Decision 5: Pagination

Never return unbounded lists. Even if you have 10 records today, you will have 10,000 tomorrow.

Cursor-Based Pagination (Recommended)

GET /api/v1/users?cursor=abc123&limit=20

Response:
{
  "data": [...],
  "pagination": {
    "next_cursor": "def456",
    "has_more": true
  }
}

Pros: Consistent performance regardless of page depth. Works well with real-time data. Cons: Cannot jump to page N directly.

Offset-Based Pagination (Common but Problematic)

GET /api/v1/users?offset=40&limit=20

Pros: Simple to understand. Can jump to any page. Cons: Performance degrades at high offsets (the database still scans all skipped rows). Data shifts between pages if records are added/deleted.

For most APIs, cursor-based pagination is worth the slight complexity increase because it scales.

Decision 6: Error Response Format

Standardize error responses from day one. Every endpoint should return errors in the same format:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Email is required",
    "details": [
      {
        "field": "email",
        "message": "This field is required"
      }
    ]
  }
}

Consistent error format enables:

  • Client-side error handling that works across all endpoints
  • Automated error tracking and categorization
  • User-facing error messages derived from API responses
  • API documentation that is predictable

Without consistent errors: Every endpoint returns errors differently. Clients need special handling for each endpoint. New developers add new error formats. The client code becomes a maze of error handling special cases.

The "Do It Right Once" Checklist

Before your API serves its first customer:

  • Authentication using a proven provider (not custom)
  • All routes under /api/v1/ prefix
  • Rate limiting on all endpoints
  • Pagination on all list endpoints
  • Consistent error response format
  • Input validation on all endpoints accepting user data
  • HTTPS only
  • CORS configured for your frontend domains only
  • Request/response logging for debugging
  • Health check endpoint (/api/v1/health)

These take 2-3 days to implement at the start. Retrofitting them later costs 2-3 weeks and risks breaking existing clients.

Need Help Building?

We help agencies and SaaS teams ship web and mobile products with senior engineers and transparent delivery.