Unit 04.00: Path parameters and what belongs in a path
Paths identify a resource. Everything optional belongs somewhere else.
Four purposes, three locations
What goes in the path, the query and the headers.
The code shows a path parameter and then the rule.
from fastapi import FastAPI
from fastapi.testclient import TestClient
app = FastAPI()
@app.get("/invoices/{invoice_id}")
def get_invoice(invoice_id: str) -> dict:
return {"invoice_id": invoice_id}
client = TestClient(app)
print(client.get("/invoices/INV-1").json())
RULES = [
("identifies the resource", "path", "/invoices/INV-1"),
("filters or sorts a collection", "query", "/invoices?status=open"),
("optional, with a default", "query", "/invoices?limit=50"),
("carries credentials", "header", "Authorization"),
]
print()
for purpose, where, example in RULES:
print(f"{purpose:34} {where:8} {example}")
print("\nPaths identify. Anything optional or repeated belongs in the query.")
A path parameter is part of the resource's identity, so /invoices/INV-1 names a thing. /invoices?id=INV-1 names a collection and filters it, which is a different resource with different caching behaviour.
Credentials go in headers for the reason the security module covers: query strings are written to every access log they pass through.
The mistake this prevents
The mistake is putting an optional value in the path. It forces two route definitions or a sentinel value in the URL, and it makes the path mean different things depending on what is in it.
Takeaway
Paths identify, queries filter and page, headers carry credentials and content negotiation. Anything optional does not belong in a path.
