Building Scalable APIs - Lessons from 24+ Projects

Building Scalable APIs - Lessons from 24+ Projects
📌

Executive Summary & Key Takeaways

After building APIs for SaaS platforms, marketplaces, mobile apps, and internal tools across 24+ projects, certain patterns consistently hold up while others consistently break at scale. Here's what we've learned, with specific advice you can apply to your next project.

Table of Contents
  1. Design for the Contract, Not the Database
  2. Authentication Patterns That Hold Up
  3. Error Handling Standards
  4. Versioning and Documentation Practices
  5. Database Query Optimization
  6. Monitoring, Alerting, and Caching Layers

After building APIs for SaaS platforms, marketplaces, mobile apps, and internal tools across 24+ projects, certain patterns consistently hold up while others consistently break at scale. Here's what we've learned, with specific advice you can apply to your next project.

A good API is a promise. Design it around the contract you give consumers, not the shape of your database today.

Building APIs that last

Design for the Contract, Not the Database

Your API endpoints should reflect what clients need, not how your database is structured. When your API is a thin layer over your database schema, every schema migration becomes a potential breaking change for every consumer. Clients should never need to understand your database structure to use your API effectively.

Model your API around resources and actions that make sense from the consumer's perspective. If your frontend needs a user profile with their recent orders and address, return that as a single endpoint rather than forcing the client to make three separate calls and join them together. The N+1 problem often starts at the API design layer, not the database query layer.

Authentication Patterns That Hold Up

JWT (JSON Web Tokens) is the standard for stateless API authentication and works well for most use cases. Keep access tokens short-lived (15 minutes to 1 hour) and use refresh tokens for session persistence. Store refresh tokens in httpOnly cookies to prevent XSS access. Never store sensitive tokens in localStorage.

For API keys serving server-to-server integrations, generate cryptographically random keys, hash them before storing in the database, and allow clients to have multiple keys with revocation capability. Scope API keys to specific permissions rather than granting full access. This makes breach containment significantly easier - you revoke one key, not an entire integration.

  • JWT access tokens: short expiry (15-60 min), signed with RS256 for better security than HS256
  • Refresh token rotation: issue a new refresh token on every refresh request and invalidate the old one
  • API key hashing: store only the hashed version, show the full key once at creation
  • Rate limiting per API key or user ID: use a sliding window algorithm for accurate limiting
  • IP allowlisting for high-privilege server-to-server keys when the client IP is known and stable

Error Handling Standards

A consistent error response format is one of the highest-value things you can standardize across an API. When every endpoint returns errors in the same shape, client-side error handling becomes dramatically simpler. We use a structure with a top-level error object containing a code (machine-readable string), a message (human-readable explanation), and optionally a details array for validation errors with field-specific messages.

Use HTTP status codes correctly. 200 for success, 201 for created resources, 400 for client errors (bad input), 401 for unauthenticated, 403 for unauthorized (authenticated but no permission), 404 for not found, 422 for validation errors, and 500 for server errors. Never return a 200 with an error in the body - it breaks every client that checks status codes.

Versioning and Documentation Practices

Version your API from day one, even if you only have one client today. The cost is minimal: add /v1/ to your route paths and document it. The benefit is enormous: when you need to make a breaking change in six months, you don't have to coordinate a simultaneous migration across every client.

For documentation, OpenAPI (formerly Swagger) is the standard. Write your OpenAPI spec first (spec-first development) and generate your API stubs from the spec. This ensures documentation is never an afterthought. Tools like Redoc and Swagger UI render beautiful interactive docs directly from the spec file. Clients can test endpoints directly from the documentation.

The queries that quietly break at scale
Loading a list, then one query per row. Fifty orders becomes fifty-one queries. Eager-load the relationships you know you need.
Add them on foreign keys and any column you filter or sort by, before you go to production - not after the slow-query alerts start.
Paginate every list endpoint from day one. A query that is fine with 100 rows can take down the API at 100,000.
If you cannot see queries over 100ms, you find them when users complain. We wire this in before launch so problems surface first to you.

Database Query Optimization

The most common performance failure in production APIs is database queries that work fine in development and collapse under real load. The N+1 query problem is the most frequent offender: loading a list of 50 orders and then making a separate database query for each order's customer details produces 51 queries instead of 2.

Implement eager loading for relationships you know you'll need. Add indexes on every foreign key, every column used in WHERE clauses, and every column used in ORDER BY clauses. Use query analysis tools (EXPLAIN ANALYZE in PostgreSQL) to verify your queries are using indexes correctly. Add monitoring to catch slow queries before users report them.

  • Paginate every list endpoint from day one - never return unbounded result sets
  • Add database indexes on foreign keys and frequently filtered columns before going to production
  • Use connection pooling (PgBouncer for PostgreSQL) to handle connection overhead at scale
  • Cache read-heavy, rarely-changing data in Redis with appropriate TTLs
  • Log slow queries (over 100ms) to a monitoring system and review them weekly

Monitoring, Alerting, and Caching Layers

An API without monitoring is an API you can't improve. At minimum, track request latency (p50, p95, p99), error rates by endpoint, and throughput. Set alerts on p99 latency exceeding 500ms and error rates exceeding 1%. These thresholds give you early warning before users start complaining.

Caching is the highest-leverage tool for API performance at scale. Cache responses that are expensive to compute and don't change frequently. A cache hit on a complex aggregation query that takes 200ms in the database costs microseconds. Implement cache invalidation carefully - cache the result by a key that includes all relevant parameters, and invalidate or expire that key whenever the underlying data changes.

The best APIs are boring. They're consistent, documented, and predictable. They handle errors gracefully, they version changes, and they monitor themselves. That's what scales from 100 users to 100,000 without a rewrite.

TECHNICAL CONSULTATION

Building something similar?

Talk to our senior engineering team about your architecture, roadmap, and delivery timeline. 100% on-time delivery guarantee.

Request a Technical Review →

Related Reading

← Back to all posts