Web APIs
Resource Modeling
Most API design disasters happen right here. Developers jump straight into building endpoints without thinking through what their resources actually are. You end up with URLs like POST /getUserDataWithFollowersAndTweets or GET /api/data/process/user/123/info. These URLs scream "I didn't plan this."
Resource modeling forces you to think like your users. What concepts matter in your domain? How do they relate to each other? How would someone naturally expect to access them through a URL?
What Makes a Good Resource
Stripe charges your credit card through a handful of resources: customers, payment methods, charges, subscriptions. Each resource represents one clear concept from their business domain.A resource is not a database table. A resource is not a function. A resource is a meaningful concept that clients want to interact with. Think nouns, not verbs. Think things, not actions.
Good resources share common traits. They represent something concrete that users understand. They have a clear lifecycle - created, read, updated, deleted. They can exist independently or relate to other resources in predictable ways.
| Resource Type | What it represents | APIForge example |
|---|---|---|
| Entity Resource | Single identifiable thing with properties | A project, team member, deployment |
| Collection Resource | Group of similar entities that can be listed | All projects for a team, active deployments |
| Sub-resource | Entity that belongs to or relates to parent | Project environments, team permissions |
| Action Resource | Process or operation with side effects | Deploy action, password reset |
Resource Hierarchies and Relationships
GitHub's API shows resource relationships through URL structure. GET /repos/{owner}/{repo}/issues gets issues for a specific repository. GET /repos/{owner}/{repo}/issues/{number}/comments gets comments on that specific issue.The URL path tells you exactly how these resources connect. Repositories contain issues. Issues contain comments. The hierarchy flows naturally from left to right in the URL.
Not every relationship needs to show up in URLs though. Sometimes you model relationships through resource properties instead of nested paths. A user might belong to multiple teams, but GET /users/123 would include team IDs in the response rather than requiring GET /teams/{id}/users/{id}.
/teams/{team-id} # Team entity
/teams/{team-id}/projects # Projects collection
/teams/{team-id}/projects/{id} # Specific project
/projects/{id}/environments # Project environments
/projects/{id}/environments/{env}/deployments # Deployments in environment
/deployments/{deployment-id} # Individual deployment
/deployments/{deployment-id}/logs # Deployment logs
Resource Naming Conventions
Slack's API URLs read like natural language. GET /conversations.list gets conversation lists. POST /chat.postMessage posts a chat message. The naming follows patterns that developers can predict.Good resource names use plural nouns for collections and specific identifiers for individual items. You want URLs that make sense when read aloud. "Get all users" maps to GET /users. "Get user 123" maps to GET /users/123.
Avoid implementation details in resource names. Don't call it /user-table or /customer-records. Don't use verbs like /getUsers or /updateCustomer. The HTTP method already tells you the action.
/users/123
/projects
/projects/web-app/environments
/orders/456/line-items
Use lowercase with hyphens
Avoid verbs in URLs
Keep paths short and logical
Be consistent across your API
Resource State and Lifecycle
Twilio phone numbers have states: available, reserved, assigned, released. Each state determines what operations make sense. You can reserve an available number, assign a reserved number, or release an assigned number.Resource state drives API behavior. A draft blog post accepts edits. A published post might reject certain changes. A deleted post returns 404 errors. Your resource model needs to account for these state transitions.
Some resources have simple lifecycles - created, updated, deleted. Others have complex workflows with multiple states and business rules about valid transitions. Document these states clearly because they affect how clients interact with your API.
The APIForge deployments resource demonstrates state-driven behavior:POST /projects/web-frontend/environments/staging/deployments
Content-Type: application/json
{
"version": "v2.1.4",
"commit_sha": "a8f3d92",
"auto_promote": false
}
Resource Composition and Embedding
Google Maps API lets you request different levels of detail for the same resource. GET /place/{id} returns basic information. GET /place/{id}?fields=name,geometry,photos returns specific fields. This flexible composition reduces API calls while keeping responses focused.Resource composition decides what information gets included in API responses. Should GET /users/123 include the user's recent posts? Their team memberships? Just basic profile data?
The answer depends on client needs and performance constraints. Including too much creates bloated responses that waste bandwidth. Including too little forces clients to make multiple API calls to get complete information.
Many APIs solve this through field selection parameters or resource expansion options. Clients can request exactly the data they need without getting everything.# Basic project info
GET /projects/web-frontend
# Project with environments expanded
GET /projects/web-frontend?expand=environments
# Project with specific fields only
GET /projects/web-frontend?fields=name,status,last_deployment
Action Resources and RPC-Style Operations
Not everything maps cleanly to CRUD operations on resources. Stripe handles this with action resources like POST /charges/{id}/capture or POST /customers/{id}/sources. These URLs represent operations rather than entities.Password resets don't fit the standard resource model. Neither do email sends, report generations, or batch operations. These actions need their own resource representations even though they're really remote procedure calls.
Action resources work best when the operation has side effects or takes significant time. Quick computations can stay as query parameters, but operations that change system state or trigger workflows deserve their own endpoints.
The APIForge team handles deployment operations as action resources:POST /deployments/dep_8x9y2k/rollback
Content-Type: application/json
{
"reason": "Critical bug in payment processing",
"target_version": "v2.1.3"
}
Resource Versioning and Evolution
Twitter's API evolved from simple tweet resources to rich media objects with threads, spaces, and communities. The resource model grew without breaking existing clients through careful versioning and backward compatibility.Resources change over time. New properties get added. Old properties get deprecated. Entire resource types might get replaced with better models. Your initial resource design won't be your final design.
Plan for evolution from the start. Use resource versioning strategies that let you introduce changes gradually. Consider how clients will migrate from old resource formats to new ones.
Common Resource Modeling Mistakes
The biggest resource modeling mistake is thinking in terms of your database schema instead of your domain concepts. Just because you have a users table doesn't mean you need a users resource that exposes every database column.Resources represent concepts that matter to clients, not database implementation details. A single resource might combine data from multiple tables. Multiple resources might expose different views of the same underlying data.
Another common mistake is creating resources that are too granular or too coarse. GET /users/123/profile/name/first is too granular. GET /dashboard-data-dump is too coarse. Find the right level of abstraction for your clients.
Resources with single-use endpoints
Exposing internal implementation details
Inconsistent naming across resources
Resources that require specific call sequences
Uses consistent naming patterns
Provides appropriate levels of detail
Supports common client workflows
Evolves without breaking changes
Quiz
1. The APIForge team is designing URLs for their project management API. Which approach follows REST resource naming conventions?
2. APIForge deployments belong to specific environments and cannot exist without them. How should this relationship be modeled in the resource hierarchy?
3. APIForge needs to let users rollback deployments, which involves complex workflow logic that doesn't fit standard CRUD operations. What's the best way to model this?