# REST APIs Through Rugby Systems Thinking

In South African rugby, coordination only works because every participant operates within defined rules. Cheslin Kolbe finishing a counter-attack, Damian Willemse shifting between positions, or Kurt-Lee Arendse exploiting space all depend on shared structure: set phases, agreed signals, and predictable outcomes.

REST APIs operate on the same principle. Systems interact reliably because the rules of communication are strict and well-defined. Once those rules are understood, the system stops looking chaotic.

* * *

### What is a REST API?

An API (Application Programming Interface) is a formal contract between systems.

It defines:

*   **<mark class="bg-yellow-200 dark:bg-yellow-500/30">How requests must be structured</mark>**
    
*   **<mark class="bg-yellow-200 dark:bg-yellow-500/30">What responses will look like</mark>**
    
*   **<mark class="bg-yellow-200 dark:bg-yellow-500/30">What operations are permitted</mark>**
    

If the request follows the contract, the system responds predictably. If it doesn’t, the request fails early.

This is about a stable interface exposed to consumers.

* * *

### REST as an Architectural Style

REST (Representational State Transfer) is a set of constraints for designing APIs over HTTP.

It standardises interaction around:

*   **<mark class="bg-yellow-200 dark:bg-yellow-500/30">Resources</mark>** (things you operate on)
    
*   **<mark class="bg-yellow-200 dark:bg-yellow-500/30">Uniform operations </mark>** (HTTP methods)
    
*   **<mark class="bg-yellow-200 dark:bg-yellow-500/30">Stateless communication</mark>**
    

Because it uses HTTP, it inherits a widely understood transport mechanism already used by browsers and networks.

* * *

### API Types (Access Model, Not Behaviour)

APIs are also classified by who can access them:

*   **<mark class="bg-yellow-200 dark:bg-yellow-500/30">Public APIs</mark>** – open to external consumers with documentation and keys
    
*   **<mark class="bg-yellow-200 dark:bg-yellow-500/30">Private APIs</mark>** – internal system-to-system communication
    
*   **<mark class="bg-yellow-200 dark:bg-yellow-500/30">Partner APIs</mark>** – restricted access for approved external organisations
    

This classification is about governance and not technical design.

* * *

### Resources, URIs, and URLs

REST models everything as a **<mark class="bg-yellow-200 dark:bg-yellow-500/30">resource</mark>**: a player, a match, or a tournament.

**URI (Uniform Resource Identifier)**

A URI identifies a resource.

**<mark class="bg-yellow-200 dark:bg-yellow-500/30">Example</mark>**<mark class="bg-yellow-200 dark:bg-yellow-500/30">:</mark>

`player/911`  
It is an identifier only. It doesn't define access.

> A URI identifies something but doesn’t tell you <mark class="bg-yellow-200 dark:bg-yellow-500/30">where</mark> or <mark class="bg-yellow-200 dark:bg-yellow-500/30">how</mark> to access it.

### URL (Uniform Resource Locator)

A URL identifies and locates the resource over a network.

**<mark class="bg-yellow-200 dark:bg-yellow-500/30">Example:</mark>**

[`https://tech-journeys.com/players/kolbe`  
](https://tech-journeys.com/players/kolbe%EF%BF%BCThis)This URL identifies Cheslin Kolbe and also tells your system to fetch his data via HTTPS from this exact location.

> All URLs are URIs because they identify a resource, but not all URIs are URLs.

### Quick Rule of Thumb

1.  **URI:** identifies a resource (who/what)
    
2.  **URL:** identifies a resource **and** tells you how to locate it (who/what + where/how)
    
3.  **Relationship:** Every URL is a URI, but not every URI is a URL.
    

### Rugby Analogy Summary

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1771213348254/c6ba985c-680b-41e1-ae84-afc55082090d.png align="center")

Here’s an example written in Python:

```python
# Define a list of players (unique identifiers in the system)
player_uris = [
    "players/kolbe",
    "players/smith",
    "players/duplessis",
    "players/mapimpi"
]

# Base URL of our rugby API or website
base_url = "https://tech-journeys.com/"

# Loop through each player and generate their full URL
for uri in player_uris:
    player_url = f"{base_url}{uri}"
    print("Player URI:", uri)
    print("Player URL:", player_url)
    print("-" * 40)  # separator for readability
```

### Output

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1771212641833/4cdcc6b0-ade7-4ca9-be88-2b355212f22a.png align="left")

* * *

### HTTP Methods as System Operations

REST defines standard actions via HTTP methods.

### <mark class="bg-yellow-200 dark:bg-yellow-500/30">GET</mark> – Read State

A `GET` request ***asks*** the server ***for*** information without changing anything.

```python
import requests

# Get Cheslin Kolbe's stats
response = requests.get("https://tech-journeys.com/players/kolbe") #This URL doesn't really exist :)
player_data = response.json()
print(player_data)
```

### Output

```json
{
  "name": "Cheslin Kolbe",
  "team": "South Africa",
  "position": "Wing",
  "special_move": "Ankle Breaker",
  "world_cups": [2019, 2023]
}
```

### <mark class="bg-yellow-200 dark:bg-yellow-500/30">POST</mark> – Create Resource

A `POST` request ***sends*** ***new*** information to the server to create something that **does not *yet* exist** like adding a new player to the squad.

```python
new_player = {
    "name": "Canan Moodie",
    "team": "South Africa",
    "position": "Centre",
    "world_cups": [2023]
}

response = requests.post("https://tech-journeys.com/players", json=new_player)
print(response.status_code)  # 201 means created
```

### <mark class="bg-yellow-200 dark:bg-yellow-500/30">PUT</mark> – Updating Resource

A `PUT` request ***replaces*** or ***updates*** an **existing** resource. For example, updating a player’s position after a tactical change.

```python
updated_position = {
    "position": "Fullback"
}

response = requests.put("https://tech-journeys.com/players/kolbe", json=updated_position)
print(response.status_code)  # 200 means success
```

### <mark class="bg-yellow-200 dark:bg-yellow-500/30">DELETE</mark> — Remove Resource

A `DELETE` request ***removes*** a resource **completely**.

```python
response = requests.delete("https://tech-journeys.com/players/retired_player_id")
print(response.status_code)  # 204 means no content, successfully deleted
```

* * *

## A Simple System Design View

A REST system typically follows a simple flow:

```css
Client → API Server → Database
```

*   **<mark class="bg-yellow-200 dark:bg-yellow-500/30">Client</mark>**: browser, mobile app, or script
    
*   **<mark class="bg-yellow-200 dark:bg-yellow-500/30">API Server</mark>**: request validation, routing, business logic
    
*   **<mark class="bg-yellow-200 dark:bg-yellow-500/30">Database</mark>**: persistent storage of resources
    

The API server enforces rules and isolates clients from internal complexity.

* * *

### Data Format: JSON

APIs exchange data using JSON (JavaScript Object Notation).

<mark class="bg-yellow-200 dark:bg-yellow-500/30">Example of a player in JSON:</mark>

```json
{
  "name": "Cheslin Kolbe",
  "team": "South Africa",
  "position": "Wing",
  "special_move": "Ankle Breaker",
  "world_cups": [2019, 2023]
}
```

It is structured as key-value pairs and arrays, optimised for machine parsing and human readability.

* * *

## Security Model

APIs expose controlled entry points to systems. Without controls, they become attack surfaces.

### <mark class="bg-yellow-200 dark:bg-yellow-500/30">Authentication (Identity)</mark>

Confirms **who** is making the request.

<mark class="bg-yellow-200 dark:bg-yellow-500/30">Example:</mark>

```python
import requests

headers = {"Authorization": "Bearer YOUR_JWT_TOKEN"}
response = requests.get("https://tech-journeys.com/players/kolbe", headers=headers)
print(response.json())
```

Common mechanisms:

*   API keys
    
*   JWT (JSON Web Tokens)
    

* * *

### <mark class="bg-yellow-200 dark:bg-yellow-500/30">Authorisation (Permissions)</mark>

Determines what the authenticated identity is allowed to do. Identity alone is insufficient.

* * *

### <mark class="bg-yellow-200 dark:bg-yellow-500/30">Encryption (Transport Security)</mark>

HTTPS enforces TLS encryption between client and server. This prevents interception or tampering in transit.

* * *

### <mark class="bg-yellow-200 dark:bg-yellow-500/30">Rate Limiting (Abuse Control)</mark>

APIs enforce request thresholds (e.g., 100 requests/minute) to protect infrastructure from overload and denial-of-service patterns.

* * *

### <mark class="bg-yellow-200 dark:bg-yellow-500/30">Core Mental Model</mark>

REST becomes simpler when reduced to state transitions:

*   **GET** → observe state
    
*   **POST** → create state
    
*   **PUT** → modify state
    
*   **DELETE** → remove state
    

Everything else is implementation detail around those transitions.

* * *

## My approach to complex systems

Over time, I’ve learned that most systems only feel complex until you relate them to something familiar. Once you do that, the moving parts become easier to reason about.

A **GET** request is just observing state.  
A **POST** request is introducing change.  
Access control is no different from checking permissions at a gate.

The specific analogy doesn’t matter. It could be rugby, football, baking, or fixing cars. What matters is mapping abstract behaviour to something you already understand.

That shift in thinking turns technical concepts into something more durable. Instead of memorising terminology, you recognise patterns. And once you recognise the pattern, it’s much easier to apply it when you’re building or debugging real systems.
