Skip to content

Designing REST APIs

Imagine you’re organizing a library. You need to decide:

  • What things do we have? (books, members, loans)
  • How do we organize them? (by category, alphabetically)
  • How do people find what they need? (catalog system, clear signs)

Designing a REST API is exactly the same! You’re creating an organized system that lets other programs find, create, update, and delete data in your application.

By the end of this guide, you’ll be able to:

  • Identify the “things” (resources) in any system
  • Organize them into logical URL structures
  • Connect them with the right HTTP operations
  • Design responses that are easy to understand and navigate

REST API design is about modeling your domain as resources (nouns), not actions (verbs). Follow these 6 steps to design a clean, intuitive API.


Look at your domain and extract the nouns—the “things” your system manages.

Example Domain: Space Mission Management

“Space agencies plan missions to planets. Astronauts are assigned to missions and use spacecraft to collect scientific data.”

Let’s circle all the nouns: Space agencies plan missions to planets. Astronauts are assigned to missions and use spacecraft to collect scientific data.

Resources identified:

  • Mission — A space expedition
  • Astronaut — A crew member
  • Planet — A celestial body
  • Spacecraft — A vehicle
  • Data — Scientific information collected

Avoid action-oriented thinking:

Wrong (verbs)Right (nouns)
getMissionMission
createAstronautAstronaut
launchSpacecraftSpacecraft

Classify each resource. Think of it like a library:

TypeAnalogyExample
SingletonONE specific book by title/api/missions/apollo-11
CollectionALL books in a section/api/missions
Sub-CollectionAll chapters IN a book/api/missions/apollo-11/crew
GET /api/planets # Collection: all planets
GET /api/planets/mars # Singleton: one planet
GET /api/planets/mars/moons # Sub-collection: moons of Mars

For detailed explanations of each resource type, see Resource-Oriented Design.


Decide how resources connect to each other:

ApproachWhen to UseExample
Sub-resourcesChild depends on or belongs to parent/missions/apollo-11/crew
ReferencesResources exist independently"mission_id": "apollo-11" with _links

Quick decision: If deleting the parent should delete the child, use sub-resources. If not, use references.

# Sub-resource: crew only exists within a mission
GET /api/missions/apollo-11/crew
# Reference: astronauts exist independently, linked via ID
GET /api/astronauts/neil-armstrong
# Response includes: { "current_mission": "apollo-11", "_links": { ... } }

For more on relationship types, see Resource-Oriented Design.


Good URLs should read almost like a sentence.

  • Plural nouns for collections: /planets, /missions
  • Lowercase with hyphens: /space-stations, not /spaceStations
  • No verbs in URLs: /missions, not /getMissions
  • Identifiers for singletons: /missions/apollo-11
/api/missions # Collection
/api/missions/{id} # Singleton
/api/missions/{id}/crew # Sub-collection
/api/missions/{id}/crew/{astronaut} # Singleton in sub-collection
# Good: Max 2 levels deep
GET /api/missions/apollo-11/crew
# Avoid: Too deep, hard to use
GET /api/missions/apollo-11/crew/neil/training/physical/cardio

If you need deep data, provide it as a direct resource:

GET /api/astronauts/neil/training

Map each resource to the HTTP methods it supports:

ResourceGETPOSTPUT/PATCHDELETE
/missionsList allCreate new
/missions/{id}Get oneUpdateDelete
/missions/{id}/crewList crewAdd member
/missions/{id}/crew/{id}Get memberUpdate roleRemove

For detailed HTTP method semantics, see HTTP Verbs. For status codes, see HTTP Status Codes.


{
"id": "apollo-11",
"name": "Apollo 11",
"status": "completed",
"launch_date": "1969-07-16",
"crew_size": 3,
"_links": {
"self": "/api/missions/apollo-11",
"crew": "/api/missions/apollo-11/crew"
}
}
{
"page": 1,
"limit": 10,
"total_count": 156,
"missions": [
{ "id": "apollo-11", "name": "Apollo 11", "status": "completed" },
{ "id": "artemis-3", "name": "Artemis 3", "status": "planned" }
],
"_links": {
"self": "/api/missions?page=1",
"next": "/api/missions?page=2"
}
}
  • Include only essential fields
  • Add _links for navigation (HATEOAS)
  • Use pagination for collections
  • Keep consistent structure across resources

Use this checklist when designing your API:

StepCheck
1. ResourcesIdentified all nouns in your domain?
2. TypesSingleton, Collection, or Sub-collection?
3. RelationshipsDependencies mapped? Sub-resources vs references decided?
4. URLsPlural nouns? Lowercase with hyphens? Shallow hierarchy?
5. OperationsHTTP methods mapped correctly? Status codes defined?
6. ResponsesConcise? Links included? Pagination for collections?

Domain: Space Mission Management

Resources:
├── /api/missions # All missions
│ └── /{id} # Single mission
│ ├── /crew # Mission crew (sub-collection)
│ └── /data # Mission data (sub-collection)
├── /api/astronauts # All astronauts (independent)
│ └── /{id}
│ └── /missions # Astronaut's mission history
├── /api/planets # All planets
│ └── /{id}
│ └── /moons # Planet's moons (sub-collection)
└── /api/spacecraft # All spacecraft (independent)