DEV Community

Cover image for Under the Hood: How My API Abstraction Layer Executes Requests (Django + DRF)
GREVE Malick
GREVE Malick

Posted on

Under the Hood: How My API Abstraction Layer Executes Requests (Django + DRF)

Before diving into the code, here is a quick overview of how the system works:

In my abstraction layer, API specifications are registered in the database. At runtime, the core logic queries these details while python services (powered by requests) handle the actual call construction and execution.

To keep things digestible, this post will focus strictly on two core components:

API Registration logic

Dynamic URL construction

(Note: I'll cover methods, endpoint parameters, headers, and authentication like OAuth 2.0 / API Keys in Part 2!)

  1. The Core Data Model (API) To store the configuration for each registered API, I built a dedicated Django model. It acts as the single source of truth for base URLs, authentication requirements, access rights, and quota management.

Here is what it looks like:
`class API(models.Model):
name = models.CharField(max_length=255)
description = models.TextField(blank=True, null=True)
url = models.URLField(validators=[URLValidator()])
auth_required = models.BooleanField(default=True)
created_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user_apis', null=True, blank=True)
api_key_encrypted = models.CharField(blank=True, null=True)
is_blocked = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)

# πŸ†• NEW FIELD: Quota cost per call
quota_cost = models.PositiveIntegerField(
    default=1,
    help_text="Number of credits consumed per call to this API"
)

def can_be_accessed_by(self, user):
    """Only creator or admin can access an API."""
    if not user or not user.is_authenticated:
        return False
    if user.is_superuser:
        return True
    return self.created_by == user

def __str__(self):
    return f"{self.name} (CoΓ»t: {self.quota_cost} crΓ©dit{'s' if self.quota_cost > 1 else ''})"`
Enter fullscreen mode Exit fullscreen mode
  1. URL Construction & Parameter Injection (build_url) The heart of the execution logic relies on a dynamic URL builder. It takes the base API URL, injects dynamic path parameters (e.g., /users/{user_id}), and encodes query string parameters cleanly using Python's urllib.parse.

`from urllib.parse import quote_plus, urlencode

def build_url(api_url: str, endpoint_path: str, parameters: list, user_params: dict) -> str:
# Clean up trailing/leading slashes to avoid double-slash issues (e.g., api.com//v1)
url = api_url.rstrip("/") + "/" + endpoint_path.lstrip("/")

# 1. Replace path parameters (e.g., {user_id} -> 42)
for param in parameters:
    if param.param_type == 'path':
        if param.name not in user_params:
            raise ValueError(f"Missing required path parameter: '{param.name}'")
        value = quote_plus(str(user_params[param.name]))
        url = url.replace(f"{{{param.name}}}", value)

# 2. Build and append query parameters
query_params = {
    param.name: user_params.get(param.name, param.default_value)
    for param in parameters
    if param.param_type == 'query' and user_params.get(param.name) is not None
}

if query_params:
    connector = '&' if '?' in url else '?'
    url += connector + urlencode(query_params)

return url`
Enter fullscreen mode Exit fullscreen mode
  1. The Execution Entry Point (ExecuteEndpointView) Once the URL logic is ready, the execution flow is triggered through a dedicated Django REST Framework view. Notice how access control and quota checks are enforced at the permission layer before any execution prep happens:

`from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from django.shortcuts import get_object_or_404

class ExecuteEndpointView(APIView):
"""
POST /api/v1/apis//endpoints//execute/
"""
authentication_classes = [APIKeyAuthentication]
permission_classes = [IsAPIKeyAuthenticated, HasSufficientQuota]

def post(self, request, api_id, endpoint_id):
    # 1. Fetch and validate access to the target API
    api = get_object_or_404(API, pk=api_id, is_active=True, is_blocked=False)
    if not api.can_be_accessed_by(request.user):
        return Response({"detail": "Forbidden."}, status=status.HTTP_403_FORBIDDEN)

    # 2. Build URL & execute request
    # (URL construction -> Headers injection -> HTTP Call via requests)`
Enter fullscreen mode Exit fullscreen mode

Well, I think I've covered enough for todayβ€”I don't want to make this article too long!

Want to check out the full code? The open-source repository link is in my bio.

Want to follow the rest of the series? Feel free to hit the follow button so you don't miss Part 2!

Thanks to everyone for reading, and happy coding!

Top comments (0)