Loading learning content...
Designing an API is like designing a language. You're creating the vocabulary, grammar, and conventions that developers will use to communicate with your service. A well-designed API feels intuitive—developers can guess how things work without reading documentation. A poorly designed API causes endless confusion, support requests, and integration failures.
API design matters because:
This page synthesizes everything we've learned into coherent design principles and practices. We'll explore how to create APIs that are intuitive, consistent, secure, and evolvable.
By the end of this page, you will understand RESTful design principles, resource modeling, naming conventions, versioning strategies, rate limiting approaches, API security best practices, and documentation standards. You'll have a comprehensive framework for designing world-class APIs.
REST (Representational State Transfer) isn't a protocol—it's an architectural style with guiding constraints. Understanding these constraints helps you design APIs that are scalable, cacheable, and maintainable.
The REST Constraints:
Resource-Oriented Thinking:
REST centers on resources—the nouns of your API. A resource is any concept that can be addressed and manipulated:
/users/42/orders/users/42/orders/settings/timezoneActions become HTTP verbs on resources:
| Instead of This | Do This |
|---|---|
| POST /createUser | POST /users |
| GET /getUser?id=42 | GET /users/42 |
| POST /deleteUser/42 | DELETE /users/42 |
| POST /updateUserEmail | PATCH /users/42 |
This transformation from RPC-style (verb-focused) to REST-style (resource-focused) is fundamental to RESTful design.
Leonard Richardson proposed a maturity model for REST APIs: Level 0 (single URI, single method), Level 1 (multiple URIs for resources), Level 2 (HTTP verbs for operations), Level 3 (hypermedia controls). Most production APIs aim for Level 2; full Level 3 (HATEOAS) is rare but valuable.
Effective API design starts with thoughtful resource modeling—identifying what resources your API exposes and how they relate to each other.
Identifying Resources:
Resources typically correspond to domain entities, but not always 1-to-1 with database tables:
Nested vs. Top-Level Resources:
Decide when resources should be nested under parents:
| Approach | Example | When to Use |
|---|---|---|
| Nested | /users/42/orders | Orders tightly bound to users; always accessed in user context |
| Top-level | /orders with ?user_id=42 | Orders have independent identity; accessed without user context |
| Both | /users/42/orders AND /orders/1001 | Convenience + direct access |
Guidelines:
/a/1/b/2/c/3 is too deep)12345678910111213141516171819202122232425262728
E-commerce API Resource Model: /products # All products/products/{id} # Single product/products/{id}/reviews # Product's reviews/products/{id}/variants # Product variants (sizes, colors) /categories # All categories/categories/{id} # Single category/categories/{id}/products # Products in category /users # All users/users/{id} # Single user/users/{id}/addresses # User's addresses/users/{id}/payment-methods # User's payment methods /orders # All orders (admin access)/orders/{id} # Single order/orders/{id}/items # Order line items/orders/{id}/shipments # Order shipments /carts # Shopping carts/carts/{id}/items # Items in cart # Action-oriented resources (when CRUD doesn't fit)/checkout # Convert cart to order/password-reset # Password reset flow/search # Cross-resource searchConsistent naming is critical for API usability. Developers should be able to guess endpoint names based on patterns they've observed.
URL Path Conventions:
| Rule | Good | Avoid |
|---|---|---|
| Use nouns, not verbs | /users, /products | /getUsers, /createProduct |
| Use plural nouns for collections | /users/42 | /user/42 |
| Use lowercase | /user-profiles | /userProfiles, /User_Profiles |
| Use hyphens for multi-word | /user-profiles | /user_profiles, /userprofiles |
| No file extensions | /users/42 | /users/42.json |
| No trailing slashes | /users/42 | /users/42/ |
Query Parameter Conventions:
Query parameters should follow consistent patterns:
1234567891011121314151617181920
# Filtering: field name as parameterGET /products?category=electronics&min_price=100&max_price=500 # Sorting: explicit sort parameter with optional orderGET /products?sort=price&order=ascGET /products?sort=-price # Prefix with - for descending # Pagination: consistent namingGET /products?page=2&per_page=20GET /products?limit=20&offset=40GET /products?cursor=abc123 # Field selection: explicit fields parameterGET /users/42?fields=id,name,email # Expansion: include related resourcesGET /orders/1001?expand=customer,items # Search: use 'q' for search queryGET /products?q=wireless+headphonesJSON Field Naming:
Choose and consistently apply a case convention:
| Case | Example | Common In |
|---|---|---|
| camelCase | firstName, createdAt | JavaScript ecosystems |
| snake_case | first_name, created_at | Ruby, Python ecosystems |
| kebab-case | first-name | Rare in JSON; conflicts with math operators |
Recommendation: Match your primary consumer language. If most clients are JavaScript apps, use camelCase. If Python, use snake_case. The key is consistency—never mix cases within an API.
Always use ISO 8601 for dates and times: 2024-01-15T10:30:00Z. Include timezone (preferably UTC with 'Z' suffix). Never use locale-specific formats like 'MM/DD/YYYY'. This eliminates parsing ambiguity across clients.
APIs evolve over time. New features are added, bugs are fixed, and occasionally breaking changes are necessary. Versioning provides a mechanism to manage this evolution without disrupting existing clients.
Versioning Approaches:
| Strategy | Example | Pros | Cons |
|---|---|---|---|
| URL Path | /api/v2/users | Explicit, easy to see, cacheable | URL pollution, hard for client library migration |
| Query Parameter | /api/users?version=2 | Non-breaking URL, easy to default | Less visible, can be forgotten |
| Header | Api-Version: 2 | Clean URLs, flexible negotiation | Hidden, harder to test/debug |
| Content Negotiation | Accept: application/vnd.api.v2+json | RESTful, precise format control | Complex, verbose |
URL Path Versioning (Most Common):
1234567
# Version in URL path - most common approachGET https://api.example.com/v1/users/42GET https://api.example.com/v2/users/42 # V2 might have different response structure:# v1: {"id": 42, "name": "Alice", "email": "alice@example.com"}# v2: {"id": 42, "account": {"email": "alice@example.com"}, "profile": {"name": "Alice"}}Header-Based Versioning (Stripe's Approach):
1234567
# Request with version headerGET /v1/customers/cus_123 HTTP/1.1Host: api.stripe.comStripe-Version: 2024-01-15 # Response adapts to requested version# If version not specified, uses account's default versionGood API design minimizes breaking changes. Add new fields (don't remove old ones). Add new endpoints (don't change existing behavior). If a field meaning changes, create a new field with a new name. Well-designed APIs can evolve for years without version bumps.
Rate limiting protects APIs from abuse, ensures fair resource distribution, and maintains service quality. Every public API needs rate limiting.
Common Rate Limiting Algorithms:
| Algorithm | How It Works | Characteristics |
|---|---|---|
| Fixed Window | Count requests per fixed time window (e.g., per minute) | Simple but burst at window edges |
| Sliding Window | Count requests in rolling time window | Smoother but more complex |
| Token Bucket | Tokens refill at constant rate; requests consume tokens | Allows controlled bursts |
| Leaky Bucket | Requests enter bucket, drain at fixed rate | Constant output rate, smooths traffic |
Rate Limit Headers:
Always communicate rate limit status to clients:
12345678910111213141516171819
# Successful response with rate limit infoHTTP/1.1 200 OKX-RateLimit-Limit: 100X-RateLimit-Remaining: 98X-RateLimit-Reset: 1705312860 # Rate limit exceededHTTP/1.1 429 Too Many RequestsX-RateLimit-Limit: 100X-RateLimit-Remaining: 0X-RateLimit-Reset: 1705312860Retry-After: 45Content-Type: application/json { "error": "rate_limit_exceeded", "message": "You have exceeded the rate limit of 100 requests per minute.", "retry_after": 45}Good API clients respect rate limits gracefully: Check remaining quota before making requests, implement exponential backoff on 429 responses, spread requests over time instead of bursting, and cache responses to reduce unnecessary calls.
API security is paramount. A security breach can compromise user data, damage reputation, and violate regulations. Security must be designed in from the start, not bolted on later.
Transport Security:
Strict-Transport-Security header to prevent downgrade attacks.Authentication Security:
| Practice | Implementation |
|---|---|
| Use tokens, not passwords | Issue JWTs or opaque tokens; never transmit passwords repeatedly |
| Short token expiry | Access tokens: 15 min to 1 hour; use refresh tokens for longer sessions |
| Secure token storage | Client-side: secure storage, not localStorage; Server-side: encrypted, not logs |
| Token revocation | Maintain revocation list or use short-lived tokens |
| Scope limitation | Tokens should have minimal necessary permissions |
Input Validation and Injection Prevention:
123456789101112131415
VALIDATION CHECKLIST: ✓ Validate all input on the server (never trust client validation)✓ Validate data types, lengths, formats, and ranges✓ Use parameterized queries for database access (prevent SQL injection)✓ Escape output for context (HTML, JSON, etc.)✓ Validate Content-Type matches actual body format✓ Limit request body size (prevent DoS)✓ Validate file uploads (type, size, and scan for malware) NEVER:✗ Interpolate user input into SQL queries✗ Execute user-provided code (eval)✗ Include user input in system commands✗ Trust client-reported Content-Type without validationAuthorization and Access Control:
The OWASP API Security Top 10 lists common API vulnerabilities: Broken Object Level Authorization, Broken Authentication, Excessive Data Exposure, Lack of Resources & Rate Limiting, Broken Function Level Authorization, Mass Assignment, Security Misconfiguration, Injection, Improper Assets Management, and Insufficient Logging & Monitoring. Study and test for these.
An API is only as good as its documentation. Developers evaluate APIs by their docs before writing any code. Poor documentation is a dealbreaker.
Documentation Components:
API Reference Best Practices:
Each endpoint should document:
123456789101112131415161718192021222324252627282930313233343536373839404142434445
## Create Order Creates a new order for the authenticated user. ### Endpoint ```POST /api/v2/orders``` ### Authentication Requires Bearer token with `orders:write` scope. ### Request Body | Field | Type | Required | Description ||-------|------|----------|-------------|| items | array | Yes | Array of order items || items[].product_id | string | Yes | Product identifier || items[].quantity | integer | Yes | Quantity (1-99) || shipping_address_id | string | Yes | Address for delivery || notes | string | No | Order notes (max 500 chars) | ### Response **201 Created**```json{ "id": "ord_abc123", "status": "pending", "items": [...], "total": 99.99, "created_at": "2024-01-15T10:30:00Z"}``` ### Errors | Status | Code | Description ||--------|------|-------------|| 400 | invalid_request | Malformed request body || 401 | unauthorized | Invalid or missing token || 422 | validation_error | Request failed validation || 422 | insufficient_stock | Product out of stock |Modern APIs provide interactive documentation (Swagger UI, Redocly, Postman) where developers can explore endpoints and make real API calls without leaving the docs. This 'try it now' capability dramatically improves developer experience and reduces time to first successful integration.
API design is both an art and a science. It requires technical knowledge, empathy for developers, and long-term thinking. Well-designed APIs become invisible—developers work with them effortlessly. Poorly designed APIs become constant friction.
Module Conclusion:
This module has taken you on a comprehensive journey through APIs and protocols at the application layer. You've learned:
With this knowledge, you can confidently consume existing APIs, design new ones, and evaluate API quality like a seasoned engineer.
Congratulations! You've completed the APIs and Protocols module. You now understand the complete lifecycle of API communication—from conceptual foundations through request/response design to overarching design principles. This knowledge is directly applicable to any software engineering role and will serve you throughout your career.