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

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',
    }
}
Expert Tip: For development, SQLite offers substantially faster read performance than connecting to a remote PostgreSQL server. This makes local testing and debugging significantly more responsive.

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}'
Common Mistake: Forgetting to include 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

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill The Form Below