Skip to content

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?


Let’s explore the common relationship patterns using a Space Exploration API.

One parent resource “owns” or “contains” many child resources.

GET /api/missions/apollo-11/astronauts
http

A mission has multiple astronauts, but each astronaut assignment belongs to that specific mission.

GET /api/planets/jupiter/moons
http

Jupiter has many moons (79!), and each moon belongs to Jupiter.

One resource has exactly one related resource.

GET /api/spacecraft/voyager-1/navigation-system
http

Each spacecraft has one navigation system, and that system belongs exclusively to that spacecraft.

Resources can be related to multiple other resources.

GET /api/astronauts/neil-armstrong/missions # Neil's missions
GET /api/missions/apollo-11/astronauts # Apollo 11's crew
http

Astronauts can go on multiple missions, and missions have multiple astronauts.


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.

GET /api/missions/artemis-3/astronauts # Get all crew members
POST /api/missions/artemis-3/astronauts # Add crew member to mission
GET /api/missions/artemis-3/astronauts/sarah # Get specific crew member
http

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
GET /api/planets/saturn/moons # Get all Saturn's moons
POST /api/planets/saturn/moons # Add newly discovered moon
GET /api/planets/saturn/moons/titan # Get Titan specifically
http

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
GET /api/missions/perseverance/data # All data from Perseverance
POST /api/missions/perseverance/data # Upload new data from mission
GET /api/missions/perseverance/data/sample-17 # Specific data sample
http

Some resources can exist independently and just reference each other.

Instead of: /api/spacecraft/apollo-csm/astronauts

Use: /api/astronauts with references

GET /api/astronauts/buzz-aldrin
http
{
"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"
}
}
json

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

Let’s design a complete API for managing space missions:

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)
plaintext
# Missions and their components
GET /api/missions # All missions
GET /api/missions/artemis-3 # Specific mission
GET /api/missions/artemis-3/astronauts # Mission crew
GET /api/missions/artemis-3/objectives # Mission objectives
GET /api/missions/artemis-3/data # Mission data
# Planets and their features
GET /api/planets # All planets
GET /api/planets/mars # Mars details
GET /api/planets/mars/moons # Mars' moons (Phobos, Deimos)
GET /api/planets/mars/atmosphere # Mars atmospheric data
# Independent astronauts with relationships
GET /api/astronauts # All astronauts
GET /api/astronauts/chris-hadfield # Chris Hadfield's profile
GET /api/astronauts/chris-hadfield/missions # His mission history
GET /api/astronauts/chris-hadfield/training # His training records
http

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)

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
# 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
http

GET /api/missions/artemis-3/astronauts
http
{
"_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"
}
}
]
}
json
GET /api/astronauts/sarah-chen
http
{
"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"
}
}
json

GET /api/missions/apollo-11/objectives
http

Use when: Child cannot exist without parent

GET /api/astronauts/neil-armstrong/missions
http

Use when: Both can exist independently

GET /api/solar-system/planets/earth/continents/north-america
http

Use when: Clear hierarchical relationships


# Good: One level of nesting
GET /api/missions/artemis-3/astronauts
# Avoid: Deep nesting gets confusing
GET /api/missions/artemis-3/astronauts/sarah/training/physical/cardio
http
# Access mission crew through mission
GET /api/missions/artemis-3/astronauts
# Access astronaut directly when you know their ID
GET /api/astronauts/sarah-chen
# Access astronauts with filtering
GET /api/astronauts?current_mission=artemis-3
http
  • Always use the same relationship style for similar cases
  • If missions have /astronauts, don’t use /crew elsewhere
  • Be consistent across your entire API

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"
}
}
json

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