Skip to content

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:

http://localhost:8000/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