REST API Introduction for GraphQL Comparison
In this tutorial, you'll learn about REST API fundamentals. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
REST (Representational State Transfer) is an architectural style where APIs expose resources as URLs and use HTTP methods for CRUD operations.
What You'll Learn
By the end of this lesson, you will understand REST's resource-oriented design, standard HTTP method usage, and how REST differs from Graphql in data fetching.
Why It Matters
REST remains the most widely adopted API style. Understanding REST deeply is essential before comparing it with GraphQL, as most GraphQL critiques reference REST patterns.
Real-World Use
Twitter API uses REST endpoints like GET /statuses/user_timeline and POST /statuses/update. Each endpoint returns a fixed response structure.
REST Request Flow
sequenceDiagram
Client->>Server: GET /users
Server->>Client: [{"id":1,"name":"A"},{"id":2,"name":"B"}]
Client->>Server: GET /users/1/posts
Server->>Client: [{"id":10,"title":"Post","user_id":1}]
Client->>Server: GET /users/2/posts
Server->>Client: [{"id":11,"title":"Another"}]
REST Endpoint Design
# rest_endpoints.py
from typing import Any, Dict, List
class RESTAPI:
def __init__(self):
self.routes: Dict[str, Dict[str, callable]] = {}
def get(self, path: str, handler: callable):
self.routes.setdefault(path, {})["GET"] = handler
def post(self, path: str, handler: callable):
self.routes.setdefault(path, {})["POST"] = handler
def handle(self, method: str, path: str, **kwargs) -> Dict:
path_handlers = self.routes.get(path, {})
handler = path_handlers.get(method)
if not handler:
return {"error": f"No {method} {path}"}
return handler(**kwargs)
api = RESTApi()
api.get("/users", lambda: {"users": [{"id": 1, "name": "Alice"}]})
api.post("/users", lambda name=None, email=None: {
"user": {"id": 2, "name": name, "email": email},
"status": "created"
})
print(api.handle("GET", "/users"))
print(api.handle("POST", "/users", name="Bob", email="bob@x.com"))
print(api.handle("GET", "/nonexistent"))
Expected output:
{'users': [{'id': 1, 'name': 'Alice'}]}
{'user': {'id': 2, 'name': 'Bob', 'email': 'bob@x.com'}, 'status': 'created'}
{'error': 'No GET /nonexistent'}
REST Response Structure
# rest_responses.py
from typing import Any, Dict, List
class RESTResponse:
@staticmethod
def success(data: Any, status: int = 200) -> Dict:
return {"status": status, "data": data, "_format": "rest"}
@staticmethod
def error(message: str, status: int = 400) -> Dict:
return {"status": status, "error": message, "_format": "rest"}
@staticmethod
def paginated(items: List, page: int, per_page: int, total: int) -> Dict:
return {
"status": 200,
"data": items,
"pagination": {
"page": page,
"per_page": per_page,
"total": total,
"pages": (total + per_page - 1) // per_page,
},
"_format": "rest",
}
resp = RESTResponse()
print(resp.success({"id": 1, "name": "Alice"}))
print(resp.error("User not found", 404))
print(resp.paginated([{"id": 1}], page=1, per_page=10, total=25))
Expected output:
{'status': 200, 'data': {'id': 1, 'name': 'Alice'}, '_format': 'rest'}
{'status': 404, 'error': 'User not found', '_format': 'rest'}
{'status': 200, 'data': [{'id': 1}], 'pagination': {'page': 1, 'per_page': 10, 'total': 25, 'pages': 3}, '_format': 'rest'}
Common Mistakes
1. Not Using HTTP Methods Correctly
Using GET for creating resources or POST for reading. GET must be safe and idempotent. POST is not idempotent.
2. Too Many Endpoints
Creating a new endpoint for every data combination leads to endpoint explosion and client complexity.
3. Inconsistent Response Structure
Some endpoints return arrays, others wrap in objects. Consistent structures prevent client-side errors.
4. Ignoring HTTP Status Codes
Returning 200 for errors or 500 for validation failures. Use correct codes: 201 for create, 400 for bad request.
5. No Pagination for Lists
Returning all results in one response. Always paginate list endpoints with page, limit, and total count.
Practice Questions
1. What does REST stand for?
Representational State Transfer.
2. What HTTP method creates a resource?
POST.
3. Is GET idempotent?
Yes, multiple identical GET requests have the same effect as one.
4. How does REST handle related data?
Through nested endpoints like /users/1/posts or linking via IDs.
Challenge
Design REST endpoints for a blog with users, posts, comments, and tags. Include pagination and proper HTTP method usage.
FAQ
Mini Project: REST API Builder
# rest_builder.py
from typing import Any, Dict, List
class RESTBuilder:
def __init__(self):
self.resources = {}
def resource(self, name: str):
self.resources[name] = []
def create(self, resource: str, **data) -> Dict:
entry = {"id": len(self.resources[resource]) + 1, **data}
self.resources[resource].append(entry)
return {"status": 201, "data": entry}
def list_all(self, resource: str) -> Dict:
return {"status": 200, "data": self.resources.get(resource, []),
"count": len(self.resources.get(resource, []))}
def get(self, resource: str, id: int) -> Dict:
items = self.resources.get(resource, [])
for item in items:
if item["id"] == id:
return {"status": 200, "data": item}
return {"status": 404, "error": "Not found"}
api = RESTBuilder()
api.resource("users")
api.create("users", name="Alice", role="admin")
api.create("users", name="Bob")
print(api.list_all("users"))
print(api.get("users", 1))
print(api.get("users", 99))
Expected output:
{'status': 200, 'data': [{'id': 1, 'name': 'Alice', 'role': 'admin'}, {'id': 2, 'name': 'Bob'}], 'count': 2}
{'status': 200, 'data': {'id': 1, 'name': 'Alice', 'role': 'admin'}}
{'status': 404, 'error': 'Not found'}
What's Next
You understand REST basics. Next, learn about GraphQL, then compare data fetching in both.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro