Building a robust API backend doesn’t have to be complicated. If you are looking to quickly prototype or deploy a small-to-medium web service, combining Django REST Framework (DRF) with SQLite offers a surprisingly powerful and zero-configuration solution. This guide walks you through the entire setup, from project creation to your first API endpoint, addressing the common hurdles developers face when using lightweight databases in production-like environments. For reference on official standards, you can check the foundational documentation at https://aminalaee.dev/ for deeper architectural insights.
Table of Contents
- Prerequisites
- Step 1: Setting Up the Virtual Environment and Django
- Step 2: Installing and Configuring Django REST Framework
- Step 3: Creating Your First App and Model
- Step 4: Building the Serializer and ViewSet
- Step 5: Wiring the URLs
- Configuration Tables
- Implementation Checklist
- Frequently Asked Questions
Quick Answer: Can DRF Work Well with SQLite?
Yes. Django REST Framework works seamlessly with SQLite out of the box. For development, testing, and low-traffic production applications, SQLite provides a file-based database that requires no separate server process. This combination eliminates database setup time while maintaining full ORM capabilities. However, for high-concurrency write operations, consider migrating to PostgreSQL.
Prerequisites
Before starting, ensure you have Python 3.8 or newer installed. You will need pip (Python package installer) and basic familiarity with the command line. No prior SQL or database administration knowledge is required for SQLite.
Step 1: Setting Up the Virtual Environment and Django
Isolation prevents dependency conflicts. Create a dedicated project directory and virtual environment.
mkdir my_drf_project
cd my_drf_project
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install django djangorestframework
Now create your Django project:
django-admin startproject config .
cd config
python manage.py startapp api
Why This Matters
Virtual environments ensure your project dependencies remain separate from system-wide packages. Starting with a clean environment reduces the risk of version conflicts later.
Step 2: Installing and Configuring Django REST Framework
DRF requires a few lines in your settings. Open config/settings.py and locate INSTALLED_APPS. Add rest_framework to the list:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework', # Add this line
'api', # Your new app
]
Verify SQLite is configured (it is the default). Check the DATABASES dictionary in settings.py:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
Step 3: Creating Your First App and Model
Inside your api/models.py, define a simple model representing a task:
from django.db import models
class Task(models.Model):
title = models.CharField(max_length=200)
completed = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
Run migrations to create the database table:
python manage.py makemigrations
python manage.py migrate
SQLite will create a single file (db.sqlite3) containing your entire database schema and data. No database server needs to be running.
Step 4: Building the Serializer and ViewSet
Serializers convert complex data types (like Django model instances) into JSON. ViewSets handle the logic for listing, creating, updating, and deleting resources.
Create api/serializers.py:
from rest_framework import serializers
from .models import Task
class TaskSerializer(serializers.ModelSerializer):
class Meta:
model = Task
fields = ['id', 'title', 'completed', 'created_at']
Create api/views.py:
from rest_framework import viewsets
from .models import Task
from .serializers import TaskSerializer
class TaskViewSet(viewsets.ModelViewSet):
queryset = Task.objects.all()
serializer_class = TaskSerializer
Step 5: Wiring the URLs
Create api/urls.py:
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import TaskViewSet
router = DefaultRouter()
router.register(r'tasks', TaskViewSet)
urlpatterns = [
path('', include(router.urls)),
]
Now include the app URLs in the project-level config/urls.py:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('api.urls')),
]
Run the development server:
python manage.py runserver
Visit http://127.0.0.1:8000/api/tasks/ in your browser. You should see the browsable API interface. Try adding a task using the HTML form or a tool like curl:
curl -X POST http://127.0.0.1:8000/api/tasks/ \
-H "Content-Type: application/json" \
-d '{"title":"Learn DRF","completed":false}'
rest_framework in INSTALLED_APPS will cause a ModuleNotFoundError when the server starts. Always verify your settings file after adding DRF.Configuration Tables
Table 1: Key Files and Their Roles
| File | Purpose | Location |
|---|---|---|
settings.py |
Project-wide configuration including database and installed apps | config/settings.py |
models.py |
Defines the database structure (tables, fields, relationships) | api/models.py |
serializers.py |
Converts model instances to JSON and vice versa | api/serializers.py |
views.py |
Contains the logic for handling API requests | api/views.py |
urls.py (app) |
Maps URL patterns to views within the app | api/urls.py |
Table 2: Common SQLite vs. PostgreSQL Considerations
| Feature | SQLite | PostgreSQL |
|---|---|---|
| Setup complexity | Zero configuration (file-based) | Requires server installation and running service |
| Concurrent writes | Single writer at a time (file-level locking) | Multiple concurrent writers supported |
| Data types | Limited type affinity (text stored as string) | Rich set of native data types |
| Best use case | Development, prototyping, low-traffic apps | Production, high-traffic, enterprise apps |
| Backup | Copy the single .sqlite3 file |
Use pg_dump or replication tools |
Implementation Checklist
- Virtual environment created and activated
- Django and
djangorestframeworkinstalled viapip -
rest_frameworkadded toINSTALLED_APPS - Database engine confirmed as
sqlite3 - At least one model defined and migrated
- Serializer created for the model
- ViewSet created and registered with a router
- URL patterns wired correctly at both app and project levels
- Server runs without errors
- API endpoint returns expected JSON responses
Frequently Asked Questions
Can I use SQLite for a production API?
Yes, for low-traffic applications (fewer than ~100 simultaneous users). SQLite handles read operations efficiently. However, write-heavy workloads will suffer from file-level locking. Monitor performance and plan to migrate to PostgreSQL if concurrency becomes an issue.
How do I migrate from SQLite to PostgreSQL later?
Use Django’s built-in dumpdata command to export your data as JSON, switch the DATABASES setting to PostgreSQL, run migrations, then use loaddata to import. Always test this on a staging environment first.
Why is my API returning a 404 error?
Check that your URL patterns are correctly included. The most common cause is forgetting to include the app URLs in the project’s urls.py file. Verify that the router.urls path is active at config/urls.py.
How do I enable authentication for my API?
Add 'DEFAULT_AUTHENTICATION_CLASSES' to REST_FRAMEWORK in settings.py. For simple token authentication, include rest_framework.authtoken in INSTALLED_APPS and run migrate.
What happens if multiple users write to SQLite simultaneously?
SQLite locks the entire database file during write operations. Subsequent write requests will wait until the lock is released. For web APIs with concurrent writes exceeding five requests per second, consider switching to PostgreSQL.
Troubleshooting Common Issues
Issue: Database is locked error during migrations
Reason: Another process (like an open Django shell or server) holds a lock on the SQLite file.
Solution: Stop all running Django processes. Delete the db.sqlite3 file and re-run python manage.py migrate. Alternatively, use lsof (Linux/macOS) or Process Explorer (Windows) to identify the locking process.
Issue: Serializer does not return all fields
Reason: The fields attribute in the Meta class of the serializer is missing some model fields.
Solution: Use fields = '__all__' to include every field, or explicitly list all desired field names.
Issue: POST request returns 415 Unsupported Media Type
Reason: The client sends data with an incorrect Content-Type header.
Solution: Ensure the request header contains Content-Type: application/json. Most REST clients set this automatically, but raw curl commands often require explicit header specification.
Moving Forward After Setup
You now have a fully functional API backend using Django REST Framework and SQLite. This foundation supports adding authentication, pagination, filtering, and custom endpoints. The file-based nature of SQLite simplifies version control integration: you can commit the db.sqlite3 file for reproducible development environments. Remember to exclude the database file from production deployments if you later migrate to a more scalable database engine. Keep your serializers lean, your viewsets focused, and your configuration minimal—this approach will serve you well from prototype to production.
