API Design →
Core API Design
What is an API? REST vs SOAP?
An API (Application Programming Interface) is a contract that allows two software systems to communicate with each other. It defines:
- How requests should be made
- What data is expected
- What responses will be returned
APIs abstract internal implementation and expose only what is necessary, enabling loose coupling between systems.
REST vs SOAP
| Feature | REST | SOAP |
|---|---|---|
| Style | Architectural style | Strict protocol |
| Data format | JSON (mostly) | XML only |
| Transport | HTTP mainly | HTTP, SMTP, TCP |
| State | Stateless | Can be stateful |
| Performance | Lightweight, fast | Heavy, slow |
| Usage today | Everywhere | Mostly legacy systems |
REST is preferred for modern applications due to simplicity and performance. SOAP is used in enterprise legacy systems.
What makes an API RESTful?
An API is RESTful only if it adheres to REST constraints.
REST Architectural Constraints
1. Client–Server Architecture
- Client handles UI and user experience
- Server handles data and business logic
- Both evolve independently
2. Statelessness
- Server does not store client session state
- Each request is self-contained
3. Cacheability
- Responses specify cache rules
- Improves performance and scalability
4. Uniform Interface
This is the most critical constraint.
Explain statelessness in REST.
Statelessness means that the server does not retain any information about the client's previous requests.
Each request must contain:
- Authentication details
- Required parameters
- Contextual data
Benefits:
- Easy horizontal scaling
- Fault tolerance
- Reduced server memory usage
Common Misconception:
Using JWT does **not** make the server stateful. The state is stored on the client.
Difference between REST and RPC.
REST
- Resource-oriented
- URLs represent entities
- HTTP semantics are meaningful
Example:
RPC (Remote Procedure Call)
- Action-oriented
- URLs represent operations
- HTTP is used as a transport mechanism
Example:
REST vs GraphQL. When would you use each?
REST
- Multiple endpoints (one per resource)
- Fixed response structure
- Can lead to over-fetching or under-fetching
GraphQL
- Single endpoint
- Client specifies exactly what data it needs
- Reduces over-fetching
When to use REST:
- Simple CRUD operations
- Well-defined resources
- Easier caching
When to use GraphQL:
- Complex data requirements
- Multiple related entities
- Mobile apps (reduce bandwidth)
HTTP Methods
HTTP Methods & Semantics
HTTP methods define what action the client wants to perform on a resource. Correct usage is critical for clarity, scalability, caching, security, and correctness.
Difference Between GET, POST, PUT, PATCH, DELETE
Each HTTP method has a clear semantic meaning. Misusing them is a design flaw, not a style choice.
| Method | Purpose | Creates Resource | Modifies Resource | Idempotent | Safe |
|---|---|---|---|---|---|
| GET | Retrieve data | No | No | Yes | Yes |
| POST | Create / Trigger | Yes | Yes | No | No |
| PUT | Replace resource | Yes (if absent) | Yes (full) | Yes | No |
| PATCH | Partial update | No | Yes (partial) | Not guaranteed | No |
| DELETE | Remove resource | No | Yes | Yes | No |
What is idempotency? Which HTTP methods are idempotent?
Idempotency
An operation is idempotent if performing it multiple times results in the same system state.
Example
PUT /users/10
Multiple identical PUT requests result in the same resource state.
HTTP Methods and Idempotency
| Method | Idempotent | Explanation |
|---|---|---|
| GET | Yes | Read-only |
| PUT | Yes | Full replacement |
| DELETE | Yes | Deletes once |
| HEAD | Yes | Metadata only |
| POST | No | Creates new resource |
PUT vs PATCH. Why both exist?
PUT (Full Replacement)
- Replaces the entire resource
- Missing fields are removed
- Idempotent by definition
If address is omitted, it is deleted.
PATCH (Partial Update)
- Updates only specified fields
- Does not affect other fields
- Not strictly idempotent
Only the provided attributes change.
Why Both Exist?
| Reason | Explanation |
|---|---|
| Clarity | PUT means replace, PATCH means modify |
| Safety | PATCH avoids accidental data loss |
| Efficiency | PATCH sends less data |
| Semantics | Different intentions, different guarantees |
Can GET have a request body?
Technically yes. Practically no.
Explanation
- HTTP specification does not forbid GET request bodies
- However:
- Servers often ignore them
- Proxies may drop them
- Caching systems do not consider them
Best Practice
- Use query parameters for GET
- Use POST if request data is complex
When should POST be used instead of PUT?
Use POST When:
- Server generates the resource ID
- Operation is non-idempotent
- Action is not a full replacement
- Triggering processing (emails, payments)
Server decides:
PUT is Used When:
- Client knows the resource URI
- Entire resource state is provided
- Idempotency is required
Safe vs idempotent methods.
Safe Methods
A method is safe if it does not modify server state.
Safe methods:
- GET
- HEAD
- OPTIONS
Even if called repeatedly, they only read data.
Idempotent Methods
A method is idempotent if performing it multiple times results in the same system state.
Idempotent methods:
- GET
- PUT
- DELETE
- HEAD
Key Difference
| Concept | Meaning |
|---|---|
| Safe | No side effects |
| Idempotent | Same result on repetition |
A method can be:
- Idempotent but not safe (DELETE)
- Safe and idempotent (GET)
Authentication & Authorization
API Key vs OAuth vs JWT
1. API Key
A simple secret string passed in requests to identify the caller.
How it works:
- Server generates a unique key for each client
- Client includes key in every request (header or query param)
- Server validates the key
Pros:
- Simple to implement
- Good for server-to-server communication
- Easy to revoke
Cons:
- No user identity (only identifies the app)
- Hard to implement fine-grained permissions
- Risk if leaked
Use Case:
Machine-to-machine communication, backend services, third-party API integrations.
2. OAuth 2.0
An authorization framework that allows third-party apps to access user resources without exposing credentials.
How it works:
- User authorizes app to access their data
- Authorization server issues an access token
- App uses token to make API requests
- Token has limited scope and expiration
Flow Example (Authorization Code):
- App redirects user to authorization server
- User logs in and grants permission
- Server redirects back with authorization code
- App exchanges code for access token
- App uses token to access protected resources
Pros:
- User doesn't share password with third-party apps
- Fine-grained permissions (scopes)
- Tokens can be revoked without changing password
- Supports refresh tokens for long-lived access
Cons:
- Complex to implement
- Requires multiple round trips
- Token storage and management overhead
Use Case:
Third-party integrations (e.g., "Sign in with Google"), delegated access, social logins.
3. JWT (JSON Web Token)
A compact, self-contained token format for securely transmitting information between parties.
Structure:
How it works:
- Server creates JWT with user info and signs it
- Client stores JWT (localStorage, cookie)
- Client sends JWT with each request
- Server verifies signature and extracts data
Pros:
- Stateless (no server-side storage needed)
- Self-contained (includes user info)
- Works across multiple servers
- Easy to scale
Cons:
- Cannot be revoked before expiration
- Larger than session IDs
- Sensitive data in payload is visible (base64 encoded, not encrypted)
Use Case:
Single Sign-On (SSO), microservices authentication, mobile apps, stateless APIs.
Comparison Table
| Feature | API Key | OAuth | JWT |
|---|---|---|---|
| Complexity | Simple | Complex | Moderate |
| User Identity | No | Yes | Yes |
| Stateless | No | No | Yes |
| Revocation | Easy | Easy | Hard |
| Best For | Server-to-server | Third-party access | Stateless APIs |
Where should tokens be sent?
Tokens should be sent in the Authorization header using the Bearer scheme.
✅ Recommended:
Why Authorization Header?
- Secure: Not logged in URLs or browser history
- Standard: Industry convention (RFC 6750)
- Clean: Separates auth from business logic
- CORS-friendly: Works with cross-origin requests
❌ Avoid:
1. Query Parameters
Problems:
- Logged in server logs
- Visible in browser history
- Can be leaked via Referer header
2. Request Body (for GET requests)
Problems:
- GET requests shouldn't have a body
- Breaks HTTP semantics
- Caching issues
3. Cookies (for APIs)
Cookies work for browser-based sessions but are problematic for APIs:
- CSRF vulnerability
- Not ideal for mobile/desktop apps
- Same-origin limitations
Exception: Web Applications
For traditional web apps, HttpOnly cookies are acceptable and can be more secure:
- Protected from XSS attacks
- Automatically sent by browser
- Require CSRF protection
Access token vs Refresh token
Access Token
A short-lived token used to access protected resources.
Characteristics:
- Short lifespan: 15 minutes to 1 hour
- Sent with every request
- Contains user permissions/claims
- Cannot be easily revoked (before expiry)
Refresh Token
A long-lived token used to obtain new access tokens without re-authentication.
Characteristics:
- Long lifespan: Days to months
- Used only to get new access tokens
- Stored securely (database)
- Can be revoked
Flow:
- User logs in → Server returns access token + refresh token
- Client uses access token for API requests
- Access token expires
- Client sends refresh token to get new access token
- Server validates refresh token and issues new access token
Why Use Both?
| Benefit | Explanation |
|---|---|
| Security | Short-lived access tokens limit damage if leaked |
| User Experience | Users don't need to re-login frequently |
| Revocation | Refresh tokens can be revoked from database |
| Rotation | Can implement refresh token rotation for added security |
Best Practices:
- Store refresh tokens securely (encrypted in DB)
- Implement refresh token rotation
- Set appropriate expiration times
- Revoke refresh tokens on logout
- Detect and handle refresh token reuse (security breach)
How do you secure APIs?
1. Authentication & Authorization
- Use OAuth 2.0 or JWT for authentication
- Implement role-based access control (RBAC)
- Validate permissions on every request
2. HTTPS Only
- Always use TLS/SSL encryption
- Reject HTTP requests
- Use HSTS headers
3. Input Validation
- Validate all input data
- Sanitize user inputs
- Use parameterized queries (prevent SQL injection)
- Limit request size
4. Rate Limiting
- Prevent brute force attacks
- Protect against DDoS
- Use token bucket or sliding window algorithms
5. API Keys & Secrets
- Never hardcode secrets in code
- Use environment variables
- Rotate keys regularly
- Store in secure vaults (AWS Secrets Manager, HashiCorp Vault)
6. CORS Configuration
- Whitelist specific origins
- Don't use wildcard (*) in production
- Validate Origin header
7. Security Headers
- Content-Security-Policy
- X-Content-Type-Options: nosniff
- X-Frame-Options: DENY
- Strict-Transport-Security
8. Logging & Monitoring
- Log all authentication attempts
- Monitor for suspicious patterns
- Set up alerts for anomalies
- Never log sensitive data (passwords, tokens)
9. API Versioning
- Version your APIs
- Maintain backward compatibility
- Deprecate old versions gracefully
10. Error Handling
- Don't expose sensitive info in errors
- Use generic error messages
- Log detailed errors server-side
How to prevent replay attacks?
A replay attack occurs when an attacker intercepts a valid request and resends it to gain unauthorized access or repeat an action.
Prevention Strategies:
1. Timestamps
Include a timestamp in each request and reject requests older than a threshold (e.g., 5 minutes).
Server checks:
2. Nonce (Number Used Once)
A unique value for each request. Server tracks used nonces and rejects duplicates.
Server logic:
3. Request Signing
Create a signature using request data + timestamp + secret key. Server verifies signature.
Server verifies:
4. Token Expiration
- Use short-lived tokens (15-60 minutes)
- Implement refresh token mechanism
- Expired tokens cannot be replayed
5. HTTPS Only
- Prevents interception of requests
- TLS encryption protects data in transit
- Essential for all other strategies to work
6. Idempotency Keys
For critical operations (payments, orders), use idempotency keys to ensure operations execute only once.
Best Practice: Combine Multiple Strategies
- HTTPS + Timestamp + Nonce + Short-lived tokens
- Request signing for critical operations
- Idempotency keys for financial transactions
Example: AWS API Request Signing
AWS uses a combination of:
- Timestamp (X-Amz-Date header)
- Request signing (Authorization header with signature)
- Short-lived credentials
Real-World / Scenario Questions
Design an API for a payment system
A payment system API must prioritize security, correctness, idempotency, and reliability. Performance is important, but correctness beats speed every time when money is involved.
Core Requirements
- Secure authentication
- Idempotent payment requests
- Strong validation
- Auditability
- Failure and retry handling
Key Resources
customerspaymentstransactionsrefunds
API Endpoints
Create a Payment
Headers:
Request Body:
Response:
Payment Status
Refund Payment
Critical Design Considerations
Idempotency
- Prevents duplicate charges on retries
- Mandatory for
POST /payments
Asynchronous Processing
- Payment gateways are slow and unreliable
- Use webhooks for final confirmation
Security
- HTTPS only
- Token-based auth
- Never expose internal transaction IDs
Design an API for a social media feed
A social media feed must optimize for read performance, not writes. Writes are occasional. Reads are constant.
Core Requirements
- Pagination
- Sorting by time or relevance
- Scalability
- Caching
Key Resources
userspostsfeedslikescomments
API Endpoints
Create Post
Get User Feed
Response:
Pagination Strategy
Cursor-based pagination is preferred over offset-based.
Why:
- Stable results
- Better performance
- No duplicate or missing posts
Feed Generation Approaches
Fan-out on Write
- Push post IDs to followers
- Faster reads
- Higher write cost
Fan-out on Read
- Generate feed dynamically
- Slower reads
- Lower storage cost
How would you design a file upload API?
File upload APIs must handle large payloads, network failures, and resumability.
Simple Upload (Small Files)
Large File Upload
Step 1: Initiate Upload
Response:
Step 2: Upload Chunks
Step 3: Complete Upload
Design Considerations
- Chunking
- Retry failed chunks
- Virus scanning
- Size and type validation
How do you design APIs for microservices?
Microservices APIs must be independent, loosely coupled, and resilient.
Core Principles
Service Ownership
- Each service owns its data
- No shared databases
API Contracts
- Backward compatible
- Versioned
Communication Patterns
- REST for synchronous calls
- Events for asynchronous communication
API Gateway Pattern
- Authentication
- Rate limiting
- Routing
- Aggregation
Failure Handling
- Timeouts
- Retries with backoff
- Circuit breakers
Observability
- Centralized logging
- Metrics
- Distributed tracing
API Design Best Practices Summary
1. Security First
- Always use HTTPS
- Implement proper authentication and authorization
- Validate all inputs
- Never expose sensitive internal IDs
2. Design for Reliability
- Make operations idempotent where possible
- Handle failures gracefully
- Use appropriate status codes
- Implement proper error responses
3. Optimize for Performance
- Use caching strategically
- Implement pagination for large datasets
- Consider async operations for slow processes
- Use appropriate HTTP methods
4. Plan for Scale
- Version your APIs from the start
- Design for backward compatibility
- Implement rate limiting
- Use API gateways for routing and aggregation
5. Maintain Observability
- Log all requests and responses
- Track metrics and performance
- Implement distributed tracing
- Monitor error rates and latency