API Contracts and Product Behavior
Learn how APIs express product behavior, protect service boundaries, and become reliable contracts between clients and systems.
What you will be able to do
An API is the contract between a client and a system. Good APIs make product behavior clear, failure behavior predictable, and service ownership enforceable.
An API Is a Contract
A weak API exposes whatever the database currently looks like. A strong API exposes what the product allows a client to do, what the client must provide, what the system promises in response, and what can go wrong.
| Contract part | Question | Example |
|---|---|---|
| Endpoint | What action is allowed? | POST /v1/notifications |
| Request body | What must the client provide? | recipientId, channel, templateId, variables |
| Response body | What does the system promise? | notificationId, status, acceptedAt |
| Status codes | How does failure show up? | 400 invalid input, 401 unauthenticated, 429 rate limited |
| Idempotency | What if the client retries? | Same idempotency key should not create duplicate notifications. |
Scenario: Checkout API
Imagine an ecommerce checkout flow. The client wants to place an order. The system must validate cart items, address, payment intent, inventory, discounts, and shipping method. A single vague endpoint can hide too much complexity.
| Bad contract | Problem | Better contract thinking |
|---|---|---|
| POST /checkout with arbitrary cart | Client can send invalid product state | Server validates product IDs, quantities, price version, and address. |
| Returns only success true | Client cannot guide the user after failure | Return explicit error codes like OUT_OF_STOCK or PAYMENT_ACTION_REQUIRED. |
| Creates order on every retry | Duplicate orders during network failures | Require idempotency key for order creation. |
| Exposes internal table IDs everywhere | Locks client to storage internals | Expose stable public IDs and product concepts. |
Request and Response Design
Request fields should match what the client can truthfully know. Response fields should match what the client needs to render the next product state. Do not force clients to understand internal workflows.
| Design choice | Good practice | Why |
|---|---|---|
| Names | Use product language | Clients understand orderId better than database row key. |
| Validation | Reject impossible states early | Prevents bad data from entering deeper services. |
| Errors | Use stable machine-readable error codes | Clients can show correct UI and retry behavior. |
| Pagination | Use cursor pagination for changing lists | Avoids duplicates and missing items during updates. |
| Versioning | Preserve old contract until clients migrate | Mobile and external clients cannot update instantly. |
Design principle
Design APIs from user flows and client behavior first. Storage tables, queues, and provider SDKs are implementation details behind the contract.
API Standards That Matter
| Standard area | What to decide | Production reason |
|---|---|---|
| Authentication | Who is calling? | Protects tenant and user data. |
| Authorization | What can they do? | Prevents privilege escalation. |
| Validation | What input is accepted? | Stops invalid state at the boundary. |
| Rate limiting | How much can one client call? | Protects shared infrastructure. |
| Idempotency | What happens on retry? | Prevents duplicate writes. |
| Observability | What request metadata is tracked? | Enables debugging and SLO tracking. |
Cloud API Entry Points
After the contract is clear, cloud services can implement authentication, routing, throttling, logging, and deployment. The contract should remain understandable without knowing the provider.
| Need | AWS | GCP | Azure |
|---|---|---|---|
| API gateway | API Gateway or Application Load Balancer | API Gateway or Cloud Load Balancing | API Management or Application Gateway |
| Serverless handler | Lambda | Cloud Functions or Cloud Run | Azure Functions or Container Apps |
| Container service | ECS/EKS | Cloud Run/GKE | Container Apps/AKS |
| Auth integration | Cognito/IAM/JWT authorizers | Identity Platform/IAM/IAP | Microsoft Entra ID/API Management policies |
| Observability | CloudWatch/X-Ray | Cloud Monitoring/Trace | Azure Monitor/Application Insights |
Beginner Mistakes
- Designing endpoints around database tables instead of user actions.
- Returning vague errors that clients cannot act on.
- Forgetting idempotency for write APIs that clients may retry.
- Ignoring pagination until lists become large.
- Changing API response shapes without thinking about old clients.
Guided Practice
Practice task
Design the API contract for creating a support ticket. Define endpoint, required fields, response fields, three error codes, and whether idempotency is needed.
Sample Answer
| Part | Example |
|---|---|
| Endpoint | POST /v1/support-tickets |
| Required fields | subject, description, category, requesterId |
| Response | ticketId, status, createdAt, nextExpectedResponseAt |
| Errors | INVALID_CATEGORY, REQUESTER_NOT_FOUND, RATE_LIMITED |
| Idempotency | Use an Idempotency-Key header so a retry does not create duplicate tickets. |
Before You Continue
- You should understand an API as a product contract.
- You should be able to define request, response, errors, and retry behavior.
- You should know why APIs should not leak database internals.
- You should be ready to follow what happens after a request enters the system.