The term "API-first" has been diluted by overuse. Teams declare themselves API-first because they have REST endpoints. Product managers add it to pitch decks without knowing what it means. The result is that the genuine architectural and strategic value of API-first thinking gets lost in the noise.
API-first is a design philosophy: define your APIs before you build the systems that implement them. Treat your API as your product's first-class interface — not an afterthought added to expose backend functionality to a frontend team.
Done correctly, API-first architecture enables:
- Multiple clients (web, mobile, third-party) built independently against a stable contract
- Internal teams working in parallel without coordination overhead
- External integrations and partnerships enabled by default
- Business capabilities that can be composed and reused across products
Done incorrectly — or not at all — you get a backend whose "API" is a collection of endpoints shaped entirely by the first frontend that needed them, with no consistency, no documentation, and no ability to support a second client without major rework.
The API-First Principle
The core constraint: define the contract before writing the implementation.
In practice: design the OpenAPI (Swagger) specification for a new feature before building the backend or frontend. The spec becomes the interface contract that both sides build against simultaneously. When you connect them, they work.
This inverts the typical workflow:
- ✗Backend team builds functionality
- ✗Frontend team waits, then discovers the API doesn't fit their needs
- ✗Rapid back-and-forth changes break both sides
- ✗Documentation written after the fact (or never)
- ✗Second client requires significant rework
- ✗Breaking changes happen without notice
- ✗Integrations require custom code for each partner
- ✓Design the API contract first (OpenAPI spec)
- ✓Frontend and backend develop in parallel against the spec
- ✓Changes go through a contract review process
- ✓Documentation generated from the spec — always current
- ✓Every additional client uses the same stable contract
- ✓Breaking changes are versioned and communicated
- ✓Partners integrate via self-service documentation
Designing a Great API
Resource-Oriented Design
The foundation of REST API design is resources — the nouns of your system. Define what your resources are before defining operations on them.
Good resource design follows these principles:
- Resources are plural nouns:
/users,/orders,/invoices— never/getUser,/createOrder - Resources have hierarchical relationships:
/organisations/{orgId}/users/{userId} - Operations are HTTP verbs:
GET(read),POST(create),PUT/PATCH(update),DELETE(remove) - Resources return consistent representations: the same fields, naming conventions, and types every time
The HTTP verb mapping:
| Operation | HTTP Method | Example |
|---|---|---|
| List resources | GET | GET /orders |
| Get single resource | GET | GET /orders/{id} |
| Create resource | POST | POST /orders |
| Full update | PUT | PUT /orders/{id} |
| Partial update | PATCH | PATCH /orders/{id} |
| Delete | DELETE | DELETE /orders/{id} |
Versioning
Every API will need to change. Design your versioning strategy before you publish your first endpoint, because retrofitting versioning is painful.
The recommended approach: URI versioning. Put the version in the path: /v1/orders, /v2/orders. Simple, visible, and easy to route at the gateway level.
Define "breaking change" explicitly in your API governance:
- Removing a field is a breaking change
- Renaming a field is a breaking change
- Changing a field's type is a breaking change
- Adding a required field is a breaking change
- Adding an optional field is not a breaking change
Breaking changes require a new major version. Non-breaking changes can be deployed to the existing version.
Error Handling
Consistent, informative error responses are one of the most underinvested areas of API design — and one of the most impactful for developer experience.
Use HTTP status codes correctly (401 for unauthenticated, 403 for unauthorised, 404 for not found, 422 for validation errors, 500 for server errors). Complement them with a structured error body:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{
"field": "email",
"message": "Must be a valid email address"
}
],
"requestId": "req_01HX2KBMV3Y4Z5P6Q7R8S9T0U"
}
}
The requestId is essential for support and debugging — it ties the client-facing error to a specific log line in your backend.
Authentication and Authorisation
Authentication (who are you?): Use OAuth 2.0 with OpenID Connect for user-facing APIs. Use short-lived JWT access tokens (15-minute expiry) with refresh tokens. For machine-to-machine (service-to-service), use OAuth 2.0 Client Credentials flow.
Authorisation (what can you do?): Keep authorisation logic in the application layer, not the database layer. Use a consistent pattern: check identity (authentication), check role (coarse authorisation), check resource ownership (fine-grained authorisation).
API keys are acceptable for simple integrations, but they don't carry identity information and are difficult to rotate. Use OAuth for anything requiring fine-grained access control.
API Gateway: Your First-Class Infrastructure
Every API-first architecture should have an API Gateway in front of all APIs. The gateway handles cross-cutting concerns that would otherwise be duplicated across every service:
- Authentication enforcement — verify tokens before requests reach your services
- Rate limiting — protect services from overload (by client, by endpoint, globally)
- Request routing — route to the right backend service
- Response caching — cache GET responses for appropriate TTLs
- Request/response transformation — adapt formats without touching service code
- Observability — centralised logging of every API call
Recommended: Azure API Management (full-featured, integrates with Entra ID, supports developer portal), Kong Gateway (open-source, cloud-agnostic), or AWS API Gateway.
The developer portal
Azure API Management includes a developer portal where external partners can self-service: browse API documentation, test endpoints, generate API keys, and manage their subscriptions. This turns your API into a product that partners can adopt without your team holding their hand.
OpenAPI: The Contract Format
OpenAPI 3.1 is the industry-standard specification format for REST APIs. A well-written OpenAPI spec provides:
- Machine-readable documentation
- Auto-generated client SDKs in any language
- Mock servers for frontend teams to develop against before the backend is ready
- Contract testing to verify the implementation matches the spec
- The foundation for your developer portal
The workflow:
- Write the OpenAPI spec first
- Use a mock server (e.g., Prism) so frontend can develop against it immediately
- Generate server stubs to give backend a starting point
- Implement the backend, validated against the spec in CI
- Generate client SDKs for partner integrations
Event-Driven APIs: Beyond Request-Response
REST APIs are synchronous by nature. For use cases that require notification when something changes — order status updates, payment confirmations, real-time data feeds — a request-response API is insufficient.
Webhooks are the most common pattern: your system POSTs to a client-provided endpoint when an event occurs. This is simple and widely understood, but requires clients to manage endpoint registration, signature verification, and retry logic.
Server-Sent Events (SSE) and WebSockets enable real-time streaming for UI-facing use cases: live dashboards, chat, collaborative editing.
AsyncAPI is the OpenAPI equivalent for event-driven interfaces — a specification format for describing Kafka topics, WebSocket channels, and webhook contracts. As your API surface expands to include events, AsyncAPI provides the same contract-driven rigour for asynchronous interfaces.
API Governance
As your API surface grows, governance prevents entropy:
- Design review — every new API or breaking change goes through a review against your style guide before implementation
- Style guide — documented conventions for naming, pagination, error formats, date formats, versioning
- Contract testing — automated tests in CI that verify the implementation matches the OpenAPI spec (Dredd, Schemathesis)
- Deprecation process — published timelines, developer communication, migration guidance
The goal of governance is not bureaucracy. It's predictability — the property that makes your API trustworthy enough that internal teams and external partners can build on it with confidence.
API architecture is one of my core areas of focus when working with product-led companies. If you're designing a new API layer or trying to bring consistency to an existing one, let's talk.