API Layer¶
Build REST APIs that let other applications (websites, mobile apps, AI agents) interact with your data.
What is an API?¶
An API (Application Programming Interface) is how different programs talk to each other. When a mobile app shows you your tasks, it's calling an API to get that data.
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Mobile App │ ────► │ Your API │ ────► │ Database │
│ │ │ (Aksara) │ │ │
│ "Show my │ ◄──── │ │ ◄──── │ [Tasks...] │
│ tasks" │ │ │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
Request Process Fetch
A REST API uses HTTP requests:
| HTTP Method | What It Does | Example |
|---|---|---|
GET |
Read data | Get all tasks |
POST |
Create data | Create a new task |
PUT |
Replace data | Update a whole task |
PATCH |
Modify data | Update part of a task |
DELETE |
Remove data | Delete a task |
What Aksara Gives You¶
Aksara creates a complete REST API automatically. You define your data (Models), and Aksara creates the endpoints.
from aksara.api import ModelViewSet
from myapp.models import Task
class TaskViewSet(ModelViewSet):
model = Task
That's 4 lines. You get 6 endpoints by default:
| Method | URL | What It Does |
|---|---|---|
GET |
/tasks/ |
List all tasks |
POST |
/tasks/ |
Create a task |
GET |
/tasks/stream/ |
Subscribe to task lifecycle events via SSE |
GET |
/tasks/{id}/ |
Get one task |
PATCH |
/tasks/{id}/ |
Partial update |
DELETE |
/tasks/{id}/ |
Delete a task |
Key Concepts¶
ViewSets¶
What they are: Classes that handle API requests for a model.
What they do: When someone calls GET /tasks/, the ViewSet decides what to return.
class TaskViewSet(ModelViewSet):
model = Task # Which model to expose
# Optional: customize which fields are returned
serializer_class = TaskSerializer
# Optional: who can access this?
permission_classes = [IsAuthenticated]
Every ModelViewSet also exposes a built-in GET /<prefix>/stream/ endpoint unless stream_enabled = False, so frontend clients can subscribe to model lifecycle events over server-sent events.
👉 Learn more: ViewSets
Serializers¶
What they are: Classes that convert between Python objects and JSON.
What they do:
- When receiving data: Validate it and convert JSON to Python
- When sending data: Convert Python to JSON
class TaskSerializer(ModelSerializer):
class Meta:
model = Task
fields = ["id", "title", "completed"]
read_only_fields = ["id"] # Can't be set by user
The serializer automatically:
- Validates required fields are present
- Checks data types (string, number, boolean)
- Rejects extra fields
- Formats the response
👉 Learn more: Serializers
Permissions¶
What they are: Rules about who can do what.
What they do: Check each request and allow or deny access.
from aksara.permissions import IsAuthenticated, IsAdminUser
class TaskViewSet(ModelViewSet):
model = Task
# Only logged-in users can access
permission_classes = [IsAuthenticated]
Common permission classes:
| Permission | Who Can Access |
|---|---|
AllowAny |
Everyone (even anonymous) |
IsAuthenticated |
Only logged-in users |
IsAdminUser |
Only admin users |
IsOwner |
Only the object's owner |
👉 Learn more: Permissions
Actions¶
What they are: Custom endpoints beyond basic CRUD.
When to use: When you need operations that aren't create/read/update/delete.
from aksara.api import ModelViewSet, action
class TaskViewSet(ModelViewSet):
model = Task
@action(detail=True, methods=["POST"])
async def complete(self, request, id: str):
"""
Mark a task as complete.
URL: POST /tasks/{id}/complete/
"""
task = await self.get_object(id)
task.completed = True
await task.save()
return {"status": "completed"}
Action options:
| Option | Meaning |
|---|---|
detail=True |
Operates on one item (/tasks/{id}/complete/) |
detail=False |
Operates on the collection (/tasks/complete_all/) |
methods=["POST"] |
Which HTTP methods to accept |
👉 Learn more: Actions
Routing¶
What it is: Tools that connect URLs to ViewSets.
What it does: Automatically create all the URL patterns.
from aksara import include_viewset
from myapp.views import TaskViewSet, ProjectViewSet
urlpatterns = [
TaskViewSet, # → /api/tasks/
ProjectViewSet, # → /api/projects/
]
def register_routes(app):
for viewset in urlpatterns:
include_viewset(app, viewset)
👉 Learn more: Routing
Quick Example¶
Here's a complete API for a Task model:
# models.py
from aksara import Model, fields
class Task(Model):
title = fields.String(max_length=200)
description = fields.Text(nullable=True)
completed = fields.Boolean(default=False)
created_at = fields.DateTime(auto_now_add=True)
# serializers.py
from aksara.api import ModelSerializer
from myapp.models import Task
class TaskSerializer(ModelSerializer):
class Meta:
model = Task
fields = ["id", "title", "description", "completed", "created_at"]
read_only_fields = ["id", "created_at"]
# views.py
from aksara.api import ModelViewSet, action
from aksara.permissions import IsAuthenticated
from myapp.models import Task
from myapp.serializers import TaskSerializer
class TaskViewSet(ModelViewSet):
model = Task
serializer_class = TaskSerializer
permission_classes = [IsAuthenticated]
# Filter options
filterset_fields = ["completed"]
search_fields = ["title", "description"]
ordering = ["-created_at"]
@action(detail=True, methods=["POST"])
async def toggle(self, request, id: str):
"""Toggle task completion status."""
task = await self.get_object(id)
task.completed = not task.completed
await task.save()
return {"completed": task.completed}
# urls.py
from aksara import include_viewset
from myapp.views import TaskViewSet
urlpatterns = [
TaskViewSet,
]
def register_routes(app):
for viewset in urlpatterns:
include_viewset(app, viewset)
This creates:
| Endpoint | What It Does |
|---|---|
GET /api/tasks/ |
List all tasks |
GET /api/tasks/?completed=true |
List completed tasks |
GET /api/tasks/?search=groceries |
Search tasks |
POST /api/tasks/ |
Create a task |
GET /api/tasks/{id}/ |
Get one task |
PATCH /api/tasks/{id}/ |
Update a task |
DELETE /api/tasks/{id}/ |
Delete a task |
POST /api/tasks/{id}/toggle/ |
Toggle completion |
Testing Your API¶
Interactive Documentation¶
Aksara automatically generates interactive docs at /docs:
You can try out every endpoint directly in the browser!
Using curl¶
# List tasks
curl http://localhost:8000/api/tasks/
# Create a task
curl -X POST http://localhost:8000/api/tasks/ \
-H "Content-Type: application/json" \
-d '{"title": "Buy milk"}'
# Get one task
curl http://localhost:8000/api/tasks/abc123/
# Update a task
curl -X PATCH http://localhost:8000/api/tasks/abc123/ \
-H "Content-Type: application/json" \
-d '{"title": "Buy milk", "completed": true}'
# Delete a task
curl -X DELETE http://localhost:8000/api/tasks/abc123/
Using Python¶
import httpx
# Create a client
client = httpx.Client(base_url="http://localhost:8000")
# List tasks
tasks = client.get("/api/tasks/").json()
# Create a task
new_task = client.post(
"/api/tasks/",
json={"title": "Learn Aksara"}
).json()
# Update a task
client.patch(
f"/api/tasks/{new_task['id']}/",
json={"title": "Learn Aksara", "completed": True}
)
What's Next?¶
| Guide | What You'll Learn |
|---|---|
| ViewSets | All ViewSet options and customization |
| Serializers | Data validation and transformation |
| Actions | Custom endpoints beyond CRUD |
| Permissions | Access control and security |
| Authentication | User login and tokens |
| Routing | URL configuration |
| Throttling | Rate limiting requests |