SLIDE 28: API Design Overview
Step 4: API Design
RESTful API Principles:
Design Principles:
| Principle | Implementation |
|———–|—————-|
| Resource-Based URLs | /users, /posts, /feed (not /getUsers, /createPost) |
| HTTP Methods | GET (read), POST (create), PUT (update), DELETE (delete) |
| Stateless | No server-side session state (use JWT tokens) |
| Versioned | /v1/posts (allows backward-compatible changes) |
| Idempotent | Multiple identical requests = same result |
| Paginated | Cursor-based pagination for large result sets |
SLIDE 28B: API Design Overview - Categories
API Categories:
- User Management APIs
- Registration, authentication, profile management
- Post APIs
- Create, retrieve, delete posts
- Feed APIs
- Retrieve personalized feed (most critical)
- Social Interaction APIs
- Like, comment, follow actions
SLIDE 28C: API Design Overview - Interview Tips
What to Cover in Interview:
- 8-10 representative endpoints (not all possible APIs)
- Request/response formats for each
- Authentication strategy
- Pagination approach
- Error handling
Common Mistakes:
- Returning entire database objects (select fields deliberately)
- Offset-based pagination (use cursor-based instead)
- Synchronous processing for expensive operations (use async queues)
- No versioning (breaking changes affect all clients)
SLIDE 29: User Management APIs - Register and Login
User Registration and Authentication
1. Register New User
POST /v1/users/register
Content-Type: application/json
Request Body:
{
"username": "mukul_oxford",
"email": "mukul@example.com",
"password": "SecureP@ssw0rd123",
"full_name": "Mukul Raina"
}
Response: 201 Created
{
"user_id": 12345,
"username": "mukul_oxford",
"email": "mukul@example.com",
"created_at": "2025-01-15T10:30:00Z",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
SLIDE 29B: User Management APIs - Register Validation
Validation Rules:
- Username: 3-30 characters, alphanumeric + underscore
- Email: Valid email format
- Password: Minimum 8 characters, complexity requirements
- Full name: Optional, max 100 characters
Error: 409 Conflict (username or email already exists)
{
"error": "username_taken",
"message": "Username 'mukul_oxford' is already registered"
}
SLIDE 29C: User Management APIs - Login
2. User Login
POST /v1/users/login
Content-Type: application/json
Request Body:
{
"username": "mukul_oxford",
"password": "SecureP@ssw0rd123"
}
Response: 200 OK
{
"user_id": 12345,
"username": "mukul_oxford",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "dGhpc19pc19hX3JlZnJlc2hfdG9rZW4...",
"expires_in": 3600
}
Error: 401 Unauthorized
{
"error": "invalid_credentials",
"message": "Username or password incorrect"
}
SLIDE 29D: User Management APIs - Get Profile
3. Get User Profile
GET /v1/users/{user_id}
Authorization: Bearer {access_token}
Response: 200 OK
{
"user_id": 12345,
"username": "mukul_oxford",
"full_name": "Mukul Raina",
"bio": "Software Engineer | MSc Oxford",
"profile_pic_url": "https://cdn.instagram.com/profiles/12345.jpg",
"follower_count": 10500,
"following_count": 342,
"post_count": 89,
"is_private": false,
"is_verified": false,
"created_at": "2024-01-15T10:30:00Z"
}
Field Explanations:
- follower_count, following_count, post_count: Pre-aggregated counts (not calculated on-the-fly)
- profile_pic_url: CDN URL (not database blob)
- is_private: Controls visibility of posts to non-followers
SLIDE 29E: User Management APIs - Update Profile
4. Update User Profile
PUT /v1/users/{user_id}
Authorization: Bearer {access_token}
Content-Type: application/json
Request Body:
{
"full_name": "Mukul Raina",
"bio": "Senior Engineer @ Microsoft | MSc Oxford | System Design",
"profile_pic_url": "https://cdn.instagram.com/profiles/12345_new.jpg"
}
Response: 200 OK
{
"user_id": 12345,
"updated_at": "2025-01-15T11:00:00Z"
}
Authorization:
- Users can only update their own profile
- Check: JWT token user_id == path parameter user_id
- Return 403 Forbidden if attempting to update another user’s profile
SLIDE 30: Post APIs - Create Post
Post Management APIs
1. Create Post
POST /v1/posts
Authorization: Bearer {access_token}
Content-Type: multipart/form-data
Request:
- image: [binary file, max 10MB]
- caption: "Beautiful day at Oxford"
- location: "Oxford, United Kingdom"
Response: 202 Accepted
{
"post_id": 789456,
"user_id": 12345,
"status": "processing",
"message": "Post is being processed. You will be notified when ready."
}
SLIDE 30B: Post APIs - Create Post Completion
Later (via WebSocket or polling):
{
"post_id": 789456,
"status": "published",
"image_url": "https://cdn.instagram.com/posts/789456_large.jpg",
"thumbnail_urls": {
"small": "https://cdn.instagram.com/posts/789456_small.jpg",
"medium": "https://cdn.instagram.com/posts/789456_medium.jpg",
"large": "https://cdn.instagram.com/posts/789456_large.jpg"
},
"created_at": "2025-01-15T18:45:00Z"
}
Why 202 Accepted (not 201 Created):
- Image processing is async (thumbnail generation, compression)
- Don’t block client while processing 5-10 seconds
- Return immediately with job ID
- Notify client when complete (WebSocket or push notification)
SLIDE 30C: Post APIs - Get User Posts
2. Get User’s Posts
GET /v1/users/{user_id}/posts?limit=20&cursor=abc123
Authorization: Bearer {access_token}
Response: 200 OK
{
"posts": [
{
"post_id": 789456,
"user_id": 12345,
"image_url": "https://cdn.instagram.com/posts/789456_large.jpg",
"thumbnail_urls": {...},
"caption": "Beautiful day at Oxford",
"location": "Oxford, United Kingdom",
"like_count": 234,
"comment_count": 12,
"created_at": "2025-01-15T18:45:00Z",
"liked_by_me": false
}
],
"pagination": {
"next_cursor": "def456",
"has_more": true
}
}
SLIDE 30D: Post APIs - Cursor Pagination
Cursor-Based Pagination:
cursor = base64_encode(last_post_id + timestamp)
Example:
- First request: No cursor (get most recent 20 posts)
- Response includes: next_cursor = "NzkyMTAwXzIwMjUtMDEtMTU=" (encoded)
- Second request: ?cursor=NzkyMTAwXzIwMjUtMDEtMTU= (get next 20)
Why Cursor-Based (not Offset-Based):
Offset-Based (BAD):
GET /posts?offset=20&limit=20
-- Problem: If new posts created, offset shifts, user sees duplicates
Cursor-Based (GOOD):
GET /posts?cursor=abc123&limit=20
-- Uses last_post_id as reference point, no duplicates
SLIDE 30E: Post APIs - Get Single Post
3. Get Single Post
GET /v1/posts/{post_id}
Authorization: Bearer {access_token}
Response: 200 OK
{
"post_id": 789456,
"user": {
"user_id": 12345,
"username": "mukul_oxford",
"profile_pic_url": "https://cdn.instagram.com/profiles/12345.jpg"
},
"image_url": "https://cdn.instagram.com/posts/789456_large.jpg",
"caption": "Beautiful day at Oxford",
"location": "Oxford, United Kingdom",
"like_count": 234,
"comment_count": 12,
"created_at": "2025-01-15T18:45:00Z",
"liked_by_me": false,
"comments_preview": [
{
"comment_id": 111,
"user": {"user_id": 999, "username": "alice"},
"text": "Amazing photo!",
"created_at": "2025-01-15T19:00:00Z"
}
]
}
SLIDE 30F: Post APIs - Embedded Data
Embedded Data:
- User object embedded (avoid second API call)
- Comments preview (first 2-3 comments)
- liked_by_me flag (personalization for current user)
SLIDE 30G: Post APIs - Delete Post
4. Delete Post
DELETE /v1/posts/{post_id}
Authorization: Bearer {access_token}
Response: 204 No Content
Error: 403 Forbidden (if trying to delete someone else's post)
{
"error": "forbidden",
"message": "You can only delete your own posts"
}
Authorization Check:
- Verify post belongs to authenticated user
- Only post owner can delete
Cascade Effects:
- Deletes post metadata from database
- Marks media files for deletion in S3 (async cleanup)
- Deletes all associated likes and comments (foreign key cascade)
SLIDE 31: Feed API - Request and Response
Feed Retrieval API (Most Critical Endpoint)
Get Personalized Feed
GET /v1/feed?limit=20&cursor=xyz789
Authorization: Bearer {access_token}
Response: 200 OK
{
"posts": [
{
"post_id": 555123,
"user": {
"user_id": 999,
"username": "friend_alice",
"profile_pic_url": "https://cdn.instagram.com/profiles/999.jpg",
"is_verified": false
},
"image_url": "https://cdn.instagram.com/posts/555123_large.jpg",
"thumbnail_urls": {
"small": "https://cdn.instagram.com/posts/555123_small.jpg",
"medium": "https://cdn.instagram.com/posts/555123_medium.jpg",
"large": "https://cdn.instagram.com/posts/555123_large.jpg"
},
"caption": "Great day at the park!",
"location": "Central Park, NYC",
"like_count": 156,
"comment_count": 8,
"created_at": "2025-01-15T12:00:00Z",
"liked_by_me": false,
"saved_by_me": false,
"score": 0.87
}
],
"pagination": {
"next_cursor": "def456",
"has_more": true
},
"metadata": {
"cached": true,
"generated_at": "2025-01-15T12:30:00Z"
}
}
SLIDE 31B: Feed API - Design Decisions
Key Design Decisions:
1. Embedded User Object:
- Avoids N+1 query problem (fetching user for each post separately)
- Client gets all data in one request
2. Multiple Image URLs:
- Client chooses appropriate size based on screen/network
- Mobile on cellular: Use small/medium
- Desktop on WiFi: Use large
3. Personalization Flags:
- liked_by_me: Has current user liked this post?
- saved_by_me: Has current user saved this post?
- Avoids additional API calls
SLIDE 31C: Feed API - Metadata and Pagination
4. Score Field (for debugging):
- Ranking score from feed algorithm
- Useful for A/B testing and debugging
- Removed in production (don’t expose algorithm details)
5. Metadata:
- cached: Was this feed served from cache?
- generated_at: When was feed computed?
- Useful for monitoring and debugging
Pagination:
First request: GET /feed?limit=20
- Returns 20 most recent/relevant posts
- Includes next_cursor
Subsequent requests: GET /feed?limit=20&cursor=abc123
- Returns next 20 posts after cursor
- Cursor encodes: (last_post_id, timestamp, user_id)
SLIDE 31D: Feed API - Performance
Performance Characteristics:
SLIDE 32: Social APIs - Like and Unlike
Like, Comment, and Follow APIs
1. Like a Post
POST /v1/posts/{post_id}/like
Authorization: Bearer {access_token}
Response: 201 Created
{
"success": true,
"post_id": 789456,
"like_count": 235,
"liked_at": "2025-01-15T19:00:00Z"
}
Idempotent: Second like request returns:
200 OK
{
"success": true,
"post_id": 789456,
"like_count": 235,
"message": "Already liked"
}
Idempotency:
- Liking same post twice = same result
- Database: INSERT IGNORE or ON CONFLICT DO NOTHING
- Client can safely retry without double-liking
SLIDE 32B: Social APIs - Unlike and Get Likes
2. Unlike a Post
DELETE /v1/posts/{post_id}/like
Authorization: Bearer {access_token}
Response: 200 OK
{
"success": true,
"post_id": 789456,
"like_count": 234
}
3. Get Post Likes
GET /v1/posts/{post_id}/likes?limit=50&cursor=abc
Authorization: Bearer {access_token}
Response: 200 OK
{
"likes": [
{
"user_id": 888,
"username": "bob_smith",
"profile_pic_url": "https://cdn.instagram.com/profiles/888.jpg",
"liked_at": "2025-01-15T19:00:00Z"
}
],
"total_count": 234,
"pagination": {
"next_cursor": "def",
"has_more": true
}
}
SLIDE 32C: Social APIs - Comment
4. Comment on Post
POST /v1/posts/{post_id}/comments
Authorization: Bearer {access_token}
Content-Type: application/json
Request Body:
{
"text": "This is an amazing photo! Love it."
}
Response: 201 Created
{
"comment_id": 88123,
"post_id": 789456,
"user": {
"user_id": 12345,
"username": "mukul_oxford",
"profile_pic_url": "https://cdn.instagram.com/profiles/12345.jpg"
},
"text": "This is an amazing photo! Love it.",
"created_at": "2025-01-15T19:00:00Z",
"like_count": 0
}
Validation:
- Text: 1-2,200 characters
- Rate limit: 10 comments per minute per user
SLIDE 32D: Social APIs - Get Comments
5. Get Post Comments
GET /v1/posts/{post_id}/comments?limit=20&cursor=abc
Authorization: Bearer {access_token}
Response: 200 OK
{
"comments": [
{
"comment_id": 88123,
"user": {...},
"text": "Amazing photo!",
"like_count": 5,
"created_at": "2025-01-15T19:00:00Z",
"liked_by_me": false
}
],
"total_count": 12,
"pagination": {...}
}
SLIDE 32E: Social APIs - Follow and Unfollow
6. Follow User
POST /v1/users/{user_id}/follow
Authorization: Bearer {access_token}
Response: 201 Created
{
"success": true,
"follower_id": 12345,
"followee_id": 67890,
"following": true,
"followed_at": "2025-01-15T20:00:00Z"
}
Side Effects:
- Increment followee’s follower_count
- Increment follower’s following_count
- Invalidate follower’s feed cache (will see new posts in feed)
7. Unfollow User
DELETE /v1/users/{user_id}/follow
Authorization: Bearer {access_token}
Response: 200 OK
{
"success": true,
"following": false
}
SLIDE 32F: Social APIs - Get Followers
8. Get User’s Followers
GET /v1/users/{user_id}/followers?limit=50&cursor=abc
Authorization: Bearer {access_token}
Response: 200 OK
{
"users": [
{
"user_id": 888,
"username": "alice",
"full_name": "Alice Johnson",
"profile_pic_url": "...",
"followed_by_me": false,
"follows_me": true
}
],
"total_count": 10500,
"pagination": {...}
}
Personalization:
- followed_by_me: Does current user follow this person?
- follows_me: Does this person follow current user?
SLIDE 33: API Design Summary - Conventions
API Design: Key Takeaways
RESTful Conventions Followed:
SLIDE 33B: API Design Summary - Pagination and Auth
Pagination Strategy:
Cursor-Based (not Offset-Based)
Cursor Format:
cursor = base64_encode(last_post_id + timestamp + salt)
Benefits:
- No duplicates when new data inserted
- Stable across concurrent modifications
- More efficient database queries (seek vs scan)
Authentication:
JWT (JSON Web Token) in Authorization header
Format:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Token contains:
- user_id
- issued_at timestamp
- expiration timestamp (1 hour)
Refresh token mechanism:
- Short-lived access token (1 hour)
- Long-lived refresh token (30 days)
- Client refreshes access token before expiration
SLIDE 33C: API Design Summary - Rate Limiting
Rate Limiting:
SLIDE 33D: API Design Summary - Error Handling
Error Handling:
Consistent Error Response Format:
{
"error": "error_code",
"message": "Human-readable error message",
"timestamp": "2025-01-15T19:00:00Z",
"request_id": "abc123def456"
}
SLIDE 33E: API Design Summary - Common Mistakes
Common Mistakes to Avoid: