Project: REST API for Mobile App Backend with DRF

Building complete REST API backend for mobile applications demonstrates Django REST Framework implementing authentication, user management, content APIs, file uploads, push notifications, and pagination maintaining production-ready architecture for iOS and Android apps. Mobile backends require specific functionality including token-based authentication for stateless requests, JSON responses optimized for bandwidth, image upload handling with compression, push notification integration, offline synchronization support, and API versioning enabling client updates without breaking changes. Traditional web applications use session-based authentication and HTML templates while mobile APIs require JWT tokens and JSON data structures creating different architectural requirements. Without proper mobile API design, applications suffer from slow response times, excessive data transfer consuming user bandwidth, authentication issues causing security vulnerabilities, and lack of error handling creating poor user experiences. This comprehensive tutorial builds fully-functional mobile backend from scratch including project setup with DRF and JWT authentication, user registration and login with token generation, profile management with avatar uploads, content CRUD operations returning JSON, pagination for list endpoints, filtering and search functionality, push notification integration with Firebase, file upload handling with validation, API documentation with Swagger, error handling with meaningful messages, and rate limiting preventing abuse. Real-world features include refresh token rotation maintaining security, social authentication with OAuth2, email verification confirming accounts, password reset workflows, device management tracking installations, analytics endpoints providing app insights, and admin dashboard monitoring API usage. Project demonstrates Django 6.0 best practices including serializers converting models to JSON with validation, ViewSets organizing endpoint logic, JWT authentication securing endpoints, permissions controlling access, and security measures protecting against common API vulnerabilities. This hands-on guide provides complete code building professional mobile backend from initial setup through deployment teaching practical Django REST development maintaining API performance serving millions of mobile requests.
Mobile Backend Project Setup
Project setup creates Django environment installing DRF for API functionality, JWT for authentication, and configuring CORS enabling mobile app requests. Understanding API architecture integrated with Django project structure enables building scalable mobile backends maintaining separation of concerns.
# Django 6.0 Mobile Backend API Setup
# Create virtual environment
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
# Install dependencies
pip install Django==6.0
pip install djangorestframework
pip install djangorestframework-simplejwt
pip install django-cors-headers
pip install Pillow # Image handling
pip install django-filter # Filtering
pip install drf-yasg # API documentation
pip install fcm-django # Firebase push notifications
# Create project
django-admin startproject mobile_backend
cd mobile_backend
# Create apps
python manage.py startapp users
python manage.py startapp content
python manage.py startapp notifications
# Configure settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Third-party
'rest_framework',
'rest_framework_simplejwt',
'corsheaders',
'django_filters',
'drf_yasg',
'fcm_django',
# Local apps
'users',
'content',
'notifications',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware', # Must be before CommonMiddleware
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
# REST Framework configuration
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 20,
'DEFAULT_FILTER_BACKENDS': [
'django_filters.rest_framework.DjangoFilterBackend',
'rest_framework.filters.SearchFilter',
'rest_framework.filters.OrderingFilter',
],
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle',
],
'DEFAULT_THROTTLE_RATES': {
'anon': '100/hour',
'user': '1000/hour',
},
}
# JWT configuration
from datetime import timedelta
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(hours=1),
'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
'ROTATE_REFRESH_TOKENS': True,
'BLACKLIST_AFTER_ROTATION': True,
}
# CORS configuration for mobile apps
CORS_ALLOWED_ORIGINS = [
'http://localhost:3000',
'capacitor://localhost', # Capacitor apps
'ionic://localhost', # Ionic apps
]
CORS_ALLOW_CREDENTIALS = True
# Media files
import os
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# Main URL configuration
# mobile_backend/urls.py
from django.contrib import admin
from django.urls import path, include
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
from rest_framework import permissions
schema_view = get_schema_view(
openapi.Info(
title="Mobile App API",
default_version='v1',
description="REST API for mobile application",
),
public=True,
permission_classes=[permissions.AllowAny],
)
urlpatterns = [
path('admin/', admin.site.urls),
# API documentation
path('api/docs/', schema_view.with_ui('swagger', cache_timeout=0)),
# Authentication
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain'),
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
# App endpoints
path('api/users/', include('users.urls')),
path('api/content/', include('content.urls')),
path('api/notifications/', include('notifications.urls')),
]User Authentication and Management
User API implements registration, login with JWT tokens, profile management, and avatar uploads with validation. Understanding authentication flow enables building secure mobile backends maintaining token-based authentication throughout application lifecycle.
# User models
# users/models.py
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
bio = models.TextField(max_length=500, blank=True)
avatar = models.ImageField(upload_to='avatars/', blank=True, null=True)
phone = models.CharField(max_length=20, blank=True)
birth_date = models.DateField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return f'{self.user.username} Profile'
class Device(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='devices')
device_id = models.CharField(max_length=255, unique=True)
device_type = models.CharField(max_length=20, choices=[('ios', 'iOS'), ('android', 'Android')])
fcm_token = models.CharField(max_length=255, blank=True)
app_version = models.CharField(max_length=20)
created_at = models.DateTimeField(auto_now_add=True)
last_active = models.DateTimeField(auto_now=True)
def __str__(self):
return f'{self.user.username} - {self.device_type}'
# User serializers
# users/serializers.py
from rest_framework import serializers
from django.contrib.auth.models import User
from .models import UserProfile, Device
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = ['bio', 'avatar', 'phone', 'birth_date']
class UserSerializer(serializers.ModelSerializer):
profile = UserProfileSerializer()
class Meta:
model = User
fields = ['id', 'username', 'email', 'first_name', 'last_name', 'profile']
read_only_fields = ['id']
class UserRegistrationSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True, min_length=8)
password_confirm = serializers.CharField(write_only=True)
class Meta:
model = User
fields = ['username', 'email', 'password', 'password_confirm', 'first_name', 'last_name']
def validate(self, data):
if data['password'] != data['password_confirm']:
raise serializers.ValidationError('Passwords do not match')
return data
def create(self, validated_data):
validated_data.pop('password_confirm')
user = User.objects.create_user(**validated_data)
UserProfile.objects.create(user=user)
return user
class DeviceSerializer(serializers.ModelSerializer):
class Meta:
model = Device
fields = ['device_id', 'device_type', 'fcm_token', 'app_version']
# User views
# users/views.py
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import AllowAny, IsAuthenticated
from django.contrib.auth.models import User
from .models import UserProfile, Device
from .serializers import (
UserSerializer,
UserRegistrationSerializer,
UserProfileSerializer,
DeviceSerializer
)
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
def get_permissions(self):
if self.action == 'create':
return [AllowAny()]
return [IsAuthenticated()]
def get_serializer_class(self):
if self.action == 'create':
return UserRegistrationSerializer
return UserSerializer
@action(detail=False, methods=['get', 'put', 'patch'])
def me(self, request):
"""Get or update current user profile"""
if request.method == 'GET':
serializer = self.get_serializer(request.user)
return Response(serializer.data)
serializer = self.get_serializer(request.user, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@action(detail=False, methods=['post'])
def register_device(self, request):
"""Register device for push notifications"""
serializer = DeviceSerializer(data=request.data)
if serializer.is_valid():
Device.objects.update_or_create(
user=request.user,
device_id=serializer.validated_data['device_id'],
defaults=serializer.validated_data
)
return Response({'status': 'device registered'})
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@action(detail=False, methods=['post'])
def change_password(self, request):
"""Change user password"""
old_password = request.data.get('old_password')
new_password = request.data.get('new_password')
if not request.user.check_password(old_password):
return Response(
{'error': 'Invalid old password'},
status=status.HTTP_400_BAD_REQUEST
)
request.user.set_password(new_password)
request.user.save()
return Response({'status': 'password changed'})
# URLs
# users/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import UserViewSet
router = DefaultRouter()
router.register('', UserViewSet)
urlpatterns = [
path('', include(router.urls)),
]Content API with Filtering
Content API provides CRUD operations with pagination, filtering, and searching optimizing data transfer for mobile bandwidth constraints. Understanding API design enables building efficient endpoints maintaining performance on limited mobile connections.
# Content models
# content/models.py
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField(unique=True)
class Meta:
verbose_name_plural = 'Categories'
def __str__(self):
return self.name
class Article(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
title = models.CharField(max_length=200)
content = models.TextField()
image = models.ImageField(upload_to='articles/', blank=True, null=True)
published = models.BooleanField(default=True)
views = models.IntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['-created_at']
def __str__(self):
return self.title
class Comment(models.Model):
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='comments')
user = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f'Comment by {self.user.username}'
# Content serializers
# content/serializers.py
from rest_framework import serializers
from .models import Category, Article, Comment
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ['id', 'name', 'slug']
class CommentSerializer(serializers.ModelSerializer):
user = serializers.StringRelatedField()
class Meta:
model = Comment
fields = ['id', 'user', 'content', 'created_at']
class ArticleListSerializer(serializers.ModelSerializer):
"""Lightweight serializer for list views"""
author = serializers.StringRelatedField()
category = CategorySerializer()
class Meta:
model = Article
fields = ['id', 'title', 'author', 'category', 'image', 'views', 'created_at']
class ArticleDetailSerializer(serializers.ModelSerializer):
"""Full serializer with comments"""
author = serializers.StringRelatedField()
category = CategorySerializer()
comments = CommentSerializer(many=True, read_only=True)
comments_count = serializers.SerializerMethodField()
class Meta:
model = Article
fields = ['id', 'title', 'content', 'author', 'category', 'image',
'views', 'created_at', 'updated_at', 'comments', 'comments_count']
def get_comments_count(self, obj):
return obj.comments.count()
class ArticleCreateSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = ['title', 'content', 'category', 'image', 'published']
# Content views
# content/views.py
from rest_framework import viewsets, filters, status
from rest_framework.decorators import action
from rest_framework.response import Response
from django_filters.rest_framework import DjangoFilterBackend
from .models import Article, Comment, Category
from .serializers import (
ArticleListSerializer,
ArticleDetailSerializer,
ArticleCreateSerializer,
CommentSerializer,
CategorySerializer
)
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.filter(published=True).select_related('author', 'category')
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_fields = ['category', 'author']
search_fields = ['title', 'content']
ordering_fields = ['created_at', 'views']
def get_serializer_class(self):
if self.action == 'list':
return ArticleListSerializer
elif self.action in ['create', 'update', 'partial_update']:
return ArticleCreateSerializer
return ArticleDetailSerializer
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
# Increment views
instance.views += 1
instance.save(update_fields=['views'])
serializer = self.get_serializer(instance)
return Response(serializer.data)
@action(detail=True, methods=['post'])
def add_comment(self, request, pk=None):
article = self.get_object()
serializer = CommentSerializer(data=request.data)
if serializer.is_valid():
serializer.save(article=article, user=request.user)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@action(detail=False, methods=['get'])
def trending(self, request):
"""Get trending articles by views"""
trending_articles = self.get_queryset().order_by('-views')[:10]
serializer = ArticleListSerializer(trending_articles, many=True)
return Response(serializer.data)
class CategoryViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Category.objects.all()
serializer_class = CategorySerializer
# URLs
# content/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import ArticleViewSet, CategoryViewSet
router = DefaultRouter()
router.register('articles', ArticleViewSet)
router.register('categories', CategoryViewSet)
urlpatterns = [
path('', include(router.urls)),
]Push Notifications Integration
Push notifications send alerts to mobile devices using Firebase Cloud Messaging with device token management tracking installations. Understanding notification integration enables building engagement features maintaining user retention through timely updates.
# Push notification models
# notifications/models.py
from django.db import models
from django.contrib.auth.models import User
class Notification(models.Model):
TYPES = [
('comment', 'Comment'),
('like', 'Like'),
('follow', 'Follow'),
('system', 'System'),
]
recipient = models.ForeignKey(User, on_delete=models.CASCADE, related_name='notifications')
sender = models.ForeignKey(User, on_delete=models.CASCADE, null=True, related_name='sent_notifications')
notification_type = models.CharField(max_length=20, choices=TYPES)
title = models.CharField(max_length=100)
message = models.TextField()
data = models.JSONField(default=dict)
read = models.BooleanField(default=False)
sent_push = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-created_at']
def __str__(self):
return f'{self.notification_type} for {self.recipient.username}'
# Notification utility
# notifications/utils.py
from fcm_django.models import FCMDevice
from .models import Notification
def send_push_notification(user, title, message, data=None):
"""Send push notification to all user's devices"""
# Save notification in database
notification = Notification.objects.create(
recipient=user,
title=title,
message=message,
data=data or {},
notification_type='system'
)
# Get user's devices
devices = FCMDevice.objects.filter(user=user)
if devices.exists():
devices.send_message(
title=title,
body=message,
data=data
)
notification.sent_push = True
notification.save()
return notification
# Notification views
# notifications/views.py
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from .models import Notification
from .serializers import NotificationSerializer
class NotificationViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = NotificationSerializer
def get_queryset(self):
return Notification.objects.filter(recipient=self.request.user)
@action(detail=True, methods=['post'])
def mark_read(self, request, pk=None):
notification = self.get_object()
notification.read = True
notification.save()
return Response({'status': 'marked as read'})
@action(detail=False, methods=['post'])
def mark_all_read(self, request):
self.get_queryset().update(read=True)
return Response({'status': 'all marked as read'})
@action(detail=False, methods=['get'])
def unread_count(self, request):
count = self.get_queryset().filter(read=False).count()
return Response({'count': count})
# Serializer
# notifications/serializers.py
from rest_framework import serializers
from .models import Notification
class NotificationSerializer(serializers.ModelSerializer):
sender = serializers.StringRelatedField()
class Meta:
model = Notification
fields = ['id', 'sender', 'notification_type', 'title', 'message',
'data', 'read', 'created_at']| Feature | Implementation | Purpose | Benefit |
|---|---|---|---|
| JWT Authentication | djangorestframework-simplejwt | Stateless auth | Scalable security |
| Pagination | PageNumberPagination | Limit data transfer | Fast responses |
| Filtering | django-filter | Query data | Efficient searches |
| Push Notifications | FCM + fcm-django | User engagement | Real-time alerts |
| API Documentation | drf-yasg (Swagger) | Developer reference | Easy integration |
Mobile API Best Practices
- Use JWT authentication: Implement token-based auth maintaining stateless architecture for scalability
- Implement pagination: Limit response sizes preventing excessive data transfer on mobile networks
- Optimize serializers: Use different serializers for list and detail views reducing payload sizes
- Add API versioning: Version endpoints enabling backward compatibility during app updates
- Handle errors consistently: Return meaningful error messages with proper HTTP status codes
- Implement rate limiting: Use throttling preventing abuse maintaining server stability
- Document APIs thoroughly: Generate Swagger documentation helping mobile developers integrate
- Compress responses: Enable gzip compression reducing bandwidth usage on mobile connections
- Test with mobile simulators: Test APIs with actual mobile apps ensuring compatibility
- Monitor API performance: Track response times, error rates, and usage patterns optimizing endpoints
Conclusion
Building complete REST API backend for mobile applications demonstrates Django REST Framework implementing JWT authentication maintaining stateless architecture, serializers converting models to JSON with validation, ViewSets organizing endpoint logic, pagination limiting response sizes, filtering enabling queries, and push notifications engaging users throughout app lifecycle. Project setup installs DRF with JWT for authentication, CORS headers enabling mobile requests, django-filter for querying, drf-yasg for documentation, and fcm-django for push notifications creating comprehensive mobile backend foundation. User API implements registration with password validation, login returning JWT access and refresh tokens, profile management with avatar uploads, device registration tracking installations, and password change functionality maintaining security throughout user lifecycle. Content API provides CRUD operations with lightweight serializers for list views reducing payload sizes, detailed serializers including related data, filtering by category and author, search across title and content fields, ordering by date and views, and custom actions like trending articles maintaining efficient data transfer. Push notifications integrate Firebase Cloud Messaging with device token storage, notification model persisting history, utility functions sending to all user devices, and API endpoints marking notifications read with unread count tracking maintaining engagement. Best practices include using JWT authentication for stateless requests, implementing pagination preventing excessive transfer, optimizing serializers with different list and detail versions, adding API versioning maintaining compatibility, handling errors consistently with meaningful messages, implementing rate limiting preventing abuse, documenting APIs thoroughly with Swagger, compressing responses reducing bandwidth, testing with mobile simulators ensuring compatibility, and monitoring performance tracking metrics. Understanding complete mobile backend development from authentication through push notifications integrated with Django REST Framework, JWT tokens, pagination, filtering, and documentation provides practical foundation building production-ready APIs serving millions of mobile requests maintaining performance, security, and developer experience throughout API lifecycle.
$ share --platform
$ cat /comments/ (0)
$ cat /comments/
// No comments found. Be the first!


