Resource Relationships and Sub-Resources
The Challenge: Resources Don’t Live Alone
Section titled “The Challenge: Resources Don’t Live Alone”In the real world, things are connected. A space mission involves astronauts, visits planets, uses spacecraft, and collects data. In REST APIs, we need to represent these relationships between resources clearly and intuitively.
The question: How do we design URLs and responses that show how resources relate to each other?
Types of Relationships
Section titled “Types of Relationships”Let’s explore the common relationship patterns using a Space Exploration API.
1. One-to-Many (Parent → Children)
Section titled “1. One-to-Many (Parent → Children)”One parent resource “owns” or “contains” many child resources.
Example: Mission → Astronauts
Section titled “Example: Mission → Astronauts”GET /api/missions/apollo-11/astronauts
A mission has multiple astronauts, but each astronaut assignment belongs to that specific mission.
Example: Planet → Moons
Section titled “Example: Planet → Moons”GET /api/planets/jupiter/moons
Jupiter has many moons (79!), and each moon belongs to Jupiter.
2. One-to-One (Exclusive Relationship)
Section titled “2. One-to-One (Exclusive Relationship)”One resource has exactly one related resource.
Example: Spacecraft → Navigation System
Section titled “Example: Spacecraft → Navigation System”GET /api/spacecraft/voyager-1/navigation-system
Each spacecraft has one navigation system, and that system belongs exclusively to that spacecraft.
3. Many-to-Many (Multiple Relationships)
Section titled “3. Many-to-Many (Multiple Relationships)”Resources can be related to multiple other resources.
Example: Astronauts ↔ Missions
Section titled “Example: Astronauts ↔ Missions”GET /api/astronauts/neil-armstrong/missions # Neil's missionsGET /api/missions/apollo-11/astronauts # Apollo 11's crew
Astronauts can go on multiple missions, and missions have multiple astronauts.
Sub-Resources: When to Use Nested URLs
Section titled “Sub-Resources: When to Use Nested URLs”Sub-resources are accessed through their parent resource’s URL. Use them when the child depends on the parent or is owned by the parent.
Pattern: /parent/{id}/children
Section titled “Pattern: /parent/{id}/children”Example 1: Mission Crew
Section titled “Example 1: Mission Crew”GET /api/missions/artemis-3/astronauts # Get all crew membersPOST /api/missions/artemis-3/astronauts # Add crew member to missionGET /api/missions/artemis-3/astronauts/sarah # Get specific crew member
Why use sub-resources here?
- Crew assignments belong to the mission
- You usually want crew in the context of the mission
- Adding someone to crew is mission-specific
Example 2: Planet’s Moons
Section titled “Example 2: Planet’s Moons”GET /api/planets/saturn/moons # Get all Saturn's moonsPOST /api/planets/saturn/moons # Add newly discovered moonGET /api/planets/saturn/moons/titan # Get Titan specifically
Why use sub-resources here?
- Moons orbit a specific planet
- Moons are discovered in relation to their planet
- You often want all moons of a particular planet
Example 3: Mission Data
Section titled “Example 3: Mission Data”GET /api/missions/perseverance/data # All data from PerseverancePOST /api/missions/perseverance/data # Upload new data from missionGET /api/missions/perseverance/data/sample-17 # Specific data sample
When NOT to Use Sub-Resources
Section titled “When NOT to Use Sub-Resources”Independent Resources with References
Section titled “Independent Resources with References”Some resources can exist independently and just reference each other.
Example: Astronauts and Spacecraft
Section titled “Example: Astronauts and Spacecraft”Instead of: /api/spacecraft/apollo-csm/astronauts
Use: /api/astronauts
with references
GET /api/astronauts/buzz-aldrin
{ "id": "buzz-aldrin", "name": "Edwin 'Buzz' Aldrin", "current_mission": "apollo-11", "current_spacecraft": "apollo-csm", "total_missions": 2, "_links": { "current_mission": "/api/missions/apollo-11", "current_spacecraft": "/api/spacecraft/apollo-csm", "all_missions": "/api/astronauts/buzz-aldrin/missions" }}
Why not use sub-resources?
- Astronauts exist independently of any specific spacecraft
- They can transfer between spacecraft
- You often need astronaut info without spacecraft context
Practical Examples
Section titled “Practical Examples”Space Mission Management System
Section titled “Space Mission Management System”Let’s design a complete API for managing space missions:
Resource Hierarchy
Section titled “Resource Hierarchy”Missions├── /astronauts (sub-resource: mission crew)├── /spacecraft (sub-resource: mission vehicles)├── /objectives (sub-resource: mission goals)└── /data (sub-resource: collected data)
Planets (independent)├── /moons (sub-resource: natural satellites)├── /atmosphere (sub-resource: atmospheric data)└── /surface (sub-resource: surface characteristics)
Astronauts (independent)├── /missions (reference: mission history)├── /training (sub-resource: training records)└── /medical (sub-resource: medical data)
API Endpoints
Section titled “API Endpoints”# Missions and their componentsGET /api/missions # All missionsGET /api/missions/artemis-3 # Specific missionGET /api/missions/artemis-3/astronauts # Mission crewGET /api/missions/artemis-3/objectives # Mission objectivesGET /api/missions/artemis-3/data # Mission data
# Planets and their featuresGET /api/planets # All planetsGET /api/planets/mars # Mars detailsGET /api/planets/mars/moons # Mars' moons (Phobos, Deimos)GET /api/planets/mars/atmosphere # Mars atmospheric data
# Independent astronauts with relationshipsGET /api/astronauts # All astronautsGET /api/astronauts/chris-hadfield # Chris Hadfield's profileGET /api/astronauts/chris-hadfield/missions # His mission historyGET /api/astronauts/chris-hadfield/training # His training records
Designing Relationships: Step by Step
Section titled “Designing Relationships: Step by Step”Step 1: Identify Your Resources
Section titled “Step 1: Identify Your Resources”Ask: What are the main “things” in my system?
For our space API:
- Mission (space expedition)
- Astronaut (space traveler)
- Planet (celestial body)
- Spacecraft (vehicle)
- Data (scientific information)
Step 2: Understand Dependencies
Section titled “Step 2: Understand Dependencies”Ask: Which resources depend on others?
Dependent (use sub-resources):
- Mission crew → depends on mission
- Planet’s moons → belong to planet
- Mission data → collected by specific mission
- Training records → belong to astronaut
Independent (use references):
- Astronauts → can exist without missions
- Planets → exist independently
- Spacecraft → can be reassigned
Step 3: Design the URLs
Section titled “Step 3: Design the URLs”# Parent-child relationships (sub-resources)/api/missions/{mission-id}/astronauts/api/planets/{planet-id}/moons/api/astronauts/{astronaut-id}/training
# Independent resources with references/api/astronauts # Include mission_id in response/api/spacecraft # Include current_mission_id/api/planets # Standalone
Response Design with Relationships
Section titled “Response Design with Relationships”Sub-Resource Response
Section titled “Sub-Resource Response”GET /api/missions/artemis-3/astronauts
{ "_links": { "self": "/api/missions/artemis-3/astronauts", "mission": "/api/missions/artemis-3" }, "mission_id": "artemis-3", "mission_name": "Artemis 3 - Lunar Landing", "crew_size": 4, "astronauts": [ { "id": "sarah-chen", "name": "Sarah Chen", "role": "Commander", "experience_years": 12, "_links": { "profile": "/api/astronauts/sarah-chen", "training": "/api/astronauts/sarah-chen/training" } }, { "id": "alex-kowalski", "name": "Alex Kowalski", "role": "Pilot", "experience_years": 8, "_links": { "profile": "/api/astronauts/alex-kowalski" } } ]}
Independent Resource with References
Section titled “Independent Resource with References”GET /api/astronauts/sarah-chen
{ "id": "sarah-chen", "name": "Sarah Chen", "age": 38, "nationality": "Canadian", "current_mission": "artemis-3", "current_role": "Commander", "total_space_hours": 2840, "_links": { "self": "/api/astronauts/sarah-chen", "current_mission": "/api/missions/artemis-3", "mission_crew": "/api/missions/artemis-3/astronauts", "training": "/api/astronauts/sarah-chen/training", "medical": "/api/astronauts/sarah-chen/medical", "all_missions": "/api/astronauts/sarah-chen/missions" }}
Common Relationship Patterns
Section titled “Common Relationship Patterns”1. Composition (Strong Ownership)
Section titled “1. Composition (Strong Ownership)”GET /api/missions/apollo-11/objectives
Use when: Child cannot exist without parent
2. Association (Loose Connection)
Section titled “2. Association (Loose Connection)”GET /api/astronauts/neil-armstrong/missions
Use when: Both can exist independently
3. Hierarchy (Nested Structure)
Section titled “3. Hierarchy (Nested Structure)”GET /api/solar-system/planets/earth/continents/north-america
Use when: Clear hierarchical relationships
Best Practices
Section titled “Best Practices”1. Keep URLs Shallow
Section titled “1. Keep URLs Shallow”# Good: One level of nestingGET /api/missions/artemis-3/astronauts
# Avoid: Deep nesting gets confusingGET /api/missions/artemis-3/astronauts/sarah/training/physical/cardio
2. Provide Multiple Access Patterns
Section titled “2. Provide Multiple Access Patterns”# Access mission crew through missionGET /api/missions/artemis-3/astronauts
# Access astronaut directly when you know their IDGET /api/astronauts/sarah-chen
# Access astronauts with filteringGET /api/astronauts?current_mission=artemis-3
3. Use Consistent Patterns
Section titled “3. Use Consistent Patterns”- Always use the same relationship style for similar cases
- If missions have
/astronauts
, don’t use/crew
elsewhere - Be consistent across your entire API
4. Include Navigation Links
Section titled “4. Include Navigation Links”Always help clients discover related resources:
{ "_links": { "self": "/api/missions/artemis-3", "astronauts": "/api/missions/artemis-3/astronauts", "spacecraft": "/api/missions/artemis-3/spacecraft", "data": "/api/missions/artemis-3/data", "objectives": "/api/missions/artemis-3/objectives" }}
Quick Decision Guide
Section titled “Quick Decision Guide”Use Sub-Resources (/parent/{id}/children
) when:
- Child belongs to parent (mission crew)
- Child depends on parent (planet’s moons)
- You usually access child through parent (mission data)
- Child’s lifecycle tied to parent
Use Independent Resources with References when:
- Both can exist alone (astronauts and spacecraft)
- Relationships change frequently (astronaut assignments)
- You need to access resource independently
- Many-to-many relationships