Overview
URL design is crucial for creating intuitive, maintainable, and scalable APIs. Well-designed URLs provide clear resource hierarchies, support proper HTTP methods, and enable efficient client-side caching and linking.
URL Structure Basics
RESTful URL Components
https://api.example.com/v1/users/123/posts/456/comments
┌─────────────────────┐┌──┐┌──────┐┌───┐┌─────┐┌────────┐
│ Base URL ││API││Resource││ID ││Sub- ││Action │
│ ││Ver││ ││ ││res. ││ │
└─────────────────────┘└──┘└──────┘└───┘└─────┘└────────┘
URL Encoding
- Reserved Characters: Encode special characters (
%20for space) - Unicode Support: Use UTF-8 encoding
- Case Sensitivity: URLs are case-sensitive (prefer lowercase)
- Length Limits: Keep URLs under 2048 characters
Resource-Based URLs
Collection Resources
# List all users
GET /users
# Create new user
POST /users
# Search users
GET /users?search=john&status=active
# Bulk operations
POST /users/batch
DELETE /users/batchIndividual Resources
# Get specific user
GET /users/123
# Update user
PUT /users/123
PATCH /users/123
# Delete user
DELETE /users/123
# Check existence
HEAD /users/123Sub-Resources
# User's posts
GET /users/123/posts
POST /users/123/posts
# Specific post
GET /users/123/posts/456
PUT /users/123/posts/456
# Post comments
GET /users/123/posts/456/comments
POST /users/123/posts/456/commentsHierarchical URLs
Nested Resources
# Organization hierarchy
/organizations/{orgId}/departments/{deptId}/employees/{empId}
/products/{productId}/variants/{variantId}/images/{imageId}
/projects/{projectId}/issues/{issueId}/comments/{commentId}Hierarchical Guidelines
- Depth Limit: Keep nesting to 3 levels maximum
- Relationship Strength: Use for strong, stable relationships
- Alternative Access: Provide direct access to nested resources
- Performance: Consider query complexity
Action-Based URLs
Resource Actions
# User actions
POST /users/123/activate
POST /users/123/deactivate
POST /users/123/reset-password
# Document actions
POST /documents/456/publish
POST /documents/456/archive
POST /documents/456/shareAction URL Patterns
# Verb-based actions
POST /users/{id}/send-verification
POST /orders/{id}/process-payment
POST /files/{id}/generate-thumbnail
# Noun-based actions (preferred)
POST /users/{id}/verification-emails
POST /orders/{id}/payments
POST /files/{id}/thumbnailsQuery Parameters
Filtering
# Simple filters
GET /users?status=active
GET /products?category=electronics&brand=apple
# Range filters
GET /orders?created_after=2023-01-01&created_before=2023-12-31
GET /products?price_min=100&price_max=500
# Array filters
GET /users?roles[]=admin&roles[]=moderator
GET /products?tags=wireless&tags=bluetoothSorting
# Single field sorting
GET /users?sort=name
GET /products?sort=price&order=desc
# Multiple field sorting
GET /users?sort=name,created_at&order=asc,desc
GET /products?sort=popularity desc,price ascPagination
# Offset-based
GET /users?page=2&limit=10
# Cursor-based
GET /users?cursor=abc123&limit=10
# Page-based
GET /users?page=2&per_page=10Field Selection
# Sparse fieldsets
GET /users?fields=id,name,email
GET /products?fields=id,name,price&include=category
# Field expansion
GET /users/123?expand=profile,preferences
GET /orders/456?expand=items.product,shipping_addressSearch and Advanced Queries
Search Parameters
# Full-text search
GET /articles?search=machine%20learning
# Field-specific search
GET /users?q=name:john+email:*@example.com
# Fuzzy search
GET /products?search~="wireless headphones"Advanced Filtering
# Complex queries
GET /orders?filter=status:pending,shipped&sort=-created_at
# JSON-based filters
GET /users?filter={"age":{"$gte":18},"country":"US"}
# Query DSL
GET /products?q=category:electronics AND price:<100Versioning in URLs
URL Path Versioning
# API versioning
/api/v1/users
/api/v2/users
# Resource versioning
/users/v1/profile
/posts/v2/contentVersioning Strategies
# Major version in path
/v1/users
/v2/users
# Date-based versioning
/2023-01-01/users
/2023-06-01/users
# Media type versioning
/users (with Accept header)Matrix Parameters
Matrix Parameters Usage
# Filtering with matrix params
/users;status=active;role=admin
# Multiple values
/products;category=electronics;brand=apple,samsung
# Hierarchical filtering
/organizations;country=us/departments;type=engineering/employeesMatrix vs Query Parameters
# Query parameters (preferred for most cases)
/users?status=active&role=admin
# Matrix parameters (for hierarchical filtering)
/users;status=active/roles;type=adminURL Templates
RFC 6570 URI Templates
# Simple expansion
/users/{id}
/posts/{id}/comments/{commentId}
# Query expansion
/users{?fields,limit,offset}
/search{?q,limit,sort}
# Fragment expansion
/users/{id}{#section}
/documents/{id}{#page}Template Examples
// URI template expansion
const userTemplate = '/users/{id}{?fields}';
const expanded = userTemplate
.replace('{id}', '123')
.replace('{?fields}', '?fields=name,email');
// Result: /users/123?fields=name,emailSpecial Resource Types
Singleton Resources
# User profile (one per user)
/users/123/profile
# Application configuration
/config
# Current user
/me
/users/currentBulk Operations
# Bulk create
POST /users/bulk
# Bulk update
PATCH /users/bulk
# Bulk delete
DELETE /users/bulkBatch Operations
# Batch requests
POST /batch
# With body:
{
"requests": [
{"method": "GET", "url": "/users/1"},
{"method": "POST", "url": "/users", "body": {...}}
]
}Content Negotiation
Format Specification
# Format in extension
GET /users.json
GET /users.xml
GET /reports.pdf
# Format in query
GET /users?format=json
GET /users?format=csv
# Accept header (preferred)
GET /users
Accept: application/jsonAPI Content Types
# Standard content types
Accept: application/json
Accept: application/xml
Accept: text/csv
Accept: application/pdf
# Custom content types
Accept: application/vnd.company.user.v1+json
Accept: application/vnd.company.report.v2+xmlError Handling URLs
Error Resource URLs
# Error details
GET /errors/validation/123
GET /errors/authentication/456
# Error types
GET /errors?type=validation&code=INVALID_EMAILURL Design Best Practices
Consistency
# Consistent patterns
GET /users/{id}/posts
GET /users/{id}/comments
GET /users/{id}/followers
# Avoid mixing patterns
GET /users/{id}/posts # Good
GET /posts?user_id={id} # InconsistentClarity
# Clear, descriptive names
GET /users/{id}/shipping-addresses
GET /orders/{id}/fulfillment-status
# Avoid abbreviations
GET /users/{id}/addr # Unclear
GET /orders/{id}/status # AmbiguousStability
# Stable URLs
/api/v1/users/{id}
/users/{id} # Avoid if planning versioning
/v1/users/{id} # Avoid if changing base pathsSEO and Discovery
URL-Friendly Design
# Human-readable URLs
GET /articles/machine-learning-basics
GET /products/wireless-headphones
# Avoid
GET /articles/123
GET /products?category=electronics&id=456Link Relations
# Link headers
Link: </users/123>; rel="self"
Link: </users/123/posts>; rel="related"
Link: </users?page=2>; rel="next"Caching Considerations
Cache-Friendly URLs
# Static resources
GET /users/123 # Cacheable
GET /users/search?q=john # Less cacheable
# Cache-busting
GET /assets/app.js?v=1.2.3
GET /images/avatar.jpg?t=1640995200Cache Headers
# Cache control
Cache-Control: max-age=300, public
ETag: "abc123"
Last-Modified: Wed, 21 Oct 2023 07:28:00 GMTInternationalization
Localized URLs
# Language in path
/en/users
/fr/utilisateurs
/de/benutzer
# Language in subdomain
en.api.example.com/users
fr.api.example.com/utilisateursURL Encoding
# Unicode in URLs
GET /users/M%C3%A9lanie # Mélanie
GET /products/caf%C3%A9 # café
# Avoid non-ASCII in URLs when possible
GET /users/melanie
GET /products/cafeURL Rewriting and Proxies
Reverse Proxy Configuration
# URL rewriting
location /api/v1/ {
proxy_pass http://backend:8080/;
rewrite ^/api/v1/(.*) /$1 break;
}
# Path stripping
location /api/ {
proxy_pass http://api-server:3000/;
proxy_set_header X-Forwarded-Prefix /api;
}Load Balancer Setup
# Path-based routing
acl is_api path_beg /api/
use_backend api_backend if is_api
# Host-based routing
acl is_mobile hdr(host) -i mobile.api.example.com
use_backend mobile_backend if is_mobileTesting URL Patterns
URL Validation
// URL pattern testing
describe('URL Patterns', () => {
const validUrls = [
'/users',
'/users/123',
'/users/123/posts',
'/users?status=active',
'/users?page=2&limit=10'
];
const invalidUrls = [
'/Users', // Wrong case
'/users/123/', // Trailing slash
'/usr/123', // Wrong resource name
];
validUrls.forEach(url => {
it(`should accept ${url}`, () => {
expect(isValidUrl(url)).toBe(true);
});
});
});Route Testing
// Route handler testing
describe('User Routes', () => {
it('should handle user collection', async () => {
const response = await request(app)
.get('/users')
.expect(200);
expect(Array.isArray(response.body)).toBe(true);
});
it('should handle individual user', async () => {
const response = await request(app)
.get('/users/123')
.expect(200);
expect(response.body).toHaveProperty('id', '123');
});
});Common URL Patterns
E-commerce APIs
# Product catalog
GET /products
GET /products/{id}
GET /categories/{id}/products
GET /products?category=electronics&brand=apple
# Shopping cart
GET /cart
POST /cart/items
PUT /cart/items/{id}
DELETE /cart/items/{id}
# Orders
GET /orders
POST /orders
GET /orders/{id}
POST /orders/{id}/cancelSocial Media APIs
# Users and relationships
GET /users/{id}/followers
GET /users/{id}/following
POST /users/{id}/follow
DELETE /users/{id}/follow
# Content
GET /posts
GET /posts/{id}
GET /users/{id}/posts
POST /posts/{id}/like
POST /posts/{id}/commentsContent Management
# Articles and content
GET /articles
GET /articles/{slug}
GET /articles?category=tech&published=true
POST /articles/{id}/publish
POST /articles/{id}/unpublish
# Media management
GET /media
POST /media/upload
GET /media/{id}/download
DELETE /media/{id}Anti-Patterns
Verb-Based URLs
# Avoid verb-based URLs
GET /getUsers
POST /createUser
PUT /updateUser
DELETE /removeUser
# Use resource-based
GET /users
POST /users
PUT /users/{id}
DELETE /users/{id}Inconsistent Naming
# Inconsistent pluralization
GET /user/123 # Singular
GET /posts # Plural
# Inconsistent naming
GET /users
GET /Customer # Different convention
GET /order_items # Different separatorQuery Parameter Abuse
# Avoid complex logic in query params
GET /users?action=delete&id=1,2,3,4,5
# Use proper resources
DELETE /users/batch
// with body: { "ids": [1,2,3,4,5] }Deep Nesting
# Avoid deep nesting
GET /orgs/1/depts/2/teams/3/projects/4/tasks/5/comments/6
# Provide direct access
GET /comments/6
GET /comments?task=5